[Bast-commits] r5416 - in trunk/PhotoOp: bin lib lib/PhotoOp/Controller lib/PhotoOp/Model root root/static/js root/static/js/OpenLayers-2.2 root/static/js/OpenLayers-2.2/art root/static/js/OpenLayers-2.2/build root/static/js/OpenLayers-2.2/doc root/static/js/OpenLayers-2.2/examples root/static/js/OpenLayers-2.2/img root/static/js/OpenLayers-2.2/lib root/static/js/OpenLayers-2.2/lib/OpenLayers root/static/js/OpenLayers-2.2/lib/OpenLayers/Control root/static/js/OpenLayers-2.2/lib/OpenLayers/Feature root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/WMS root/static/js/OpenLayers-2.2/lib/OpenLayers/Marker root/static/js/OpenLayers-2.2/lib/OpenLayers/Popup root/static/js/OpenLayers-2.2/lib/OpenLayers/Tile root/static/js/OpenLayers-2.2/lib/Rico root/static/js/OpenLayers-2.2/tests root/static/js/OpenLayers-2.2/theme root/static/js/OpenLayers-2.2/theme/default root/static/js/OpenLayers-2.2/tools root/static/js/src root/static/js/src/animation root/static/js/src/collections root/static/js/src/compat root/static/js/src/crypto root/static/js/src/data root/static/js/src/data/format root/static/js/src/data/provider root/static/js/src/debug root/static/js/src/dnd root/static/js/src/event root/static/js/src/flash root/static/js/src/flash/flash6 root/static/js/src/flash/flash8 root/static/js/src/fx root/static/js/src/graphics root/static/js/src/graphics/color root/static/js/src/html root/static/js/src/html/images root/static/js/src/i18n root/static/js/src/io root/static/js/src/lang root/static/js/src/lfx root/static/js/src/logging root/static/js/src/math root/static/js/src/reflect root/static/js/src/rpc root/static/js/src/selection root/static/js/src/storage root/static/js/src/string root/static/js/src/text root/static/js/src/undo root/static/js/src/uri root/static/js/src/uuid root/static/js/src/validate root/static/js/src/widget root/static/js/src/widget/demoEngine root/static/js/src/widget/demoEngine/templates root/static/js/src/widget/demoEngine/templates/images root/static/js/src/widget/html root/static/js/src/widget/svg root/static/js/src/widget/templates root/static/js/src/widget/templates/buttons root/static/js/src/widget/templates/images root/static/js/src/widget/templates/images/Tree root/static/js/src/widget/vml root/static/js/src/xml

castaway at dev.catalyst.perl.org castaway at dev.catalyst.perl.org
Sat Feb 7 13:12:56 GMT 2009


Author: castaway
Date: 2009-02-07 13:12:54 +0000 (Sat, 07 Feb 2009)
New Revision: 5416

Added:
   trunk/PhotoOp/bin/copy_pic.pl
   trunk/PhotoOp/bin/create_user.pl
   trunk/PhotoOp/bin/gpsPhoto.pl
   trunk/PhotoOp/lib/PhotoOp/Model/MyFlickr.pm
   trunk/PhotoOp/root/login.tt
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/arrows.svg
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/layer-switcher-maximize.svg
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/layer-switcher-minimize.svg
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/marker.svg
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/measuring-stick-off.svg
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/measuring-stick-on.svg
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/panning-hand-off.svg
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/panning-hand-on.svg
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/slider.svg
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/zoom-world.svg
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/zoombar.svg
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/build/
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/build/OpenLayers.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/build/README.txt
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/build/build.sh
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/build/docs.sh
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/build/library.cfg
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/build/license.txt
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/build/lite.cfg
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Ajax.txt
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Bounds.txt
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Control.LayerSwitcher.txt
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Control.MouseDefaults.txt
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Control.MousePosition.txt
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Control.MouseToolbar.txt
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Control.PanZoom.txt
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Control.PanZoomBar.txt
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Control.Permalink.txt
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Control.Scale.txt
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Control.txt
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Icon.txt
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.Boxes.txt
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.EventPane.txt
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.GeoRSS.txt
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.Google.txt
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.Grid.txt
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.HTTPRequest.txt
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.Image.txt
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.KaMap.txt
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.Markers.txt
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.Text.txt
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.VirtualEarth.txt
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.WMS.txt
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.txt
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/LonLat.txt
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Map.txt
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Marker.Box.txt
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Marker.txt
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Pixel.txt
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Size.txt
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Tile.Image.txt
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Tile.WFS.txt
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Tile.txt
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Util.txt
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/authors.txt
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/customization
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/baseLayers.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/boxes.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/canvas.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/ciesin.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/click.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/controls.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/doubleSetCenter.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/example.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/freemap.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/fullScreen.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/georss.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/georss.xml
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/getfeatureinfo.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/google.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/image-layer.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/kamap.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/kamap.txt
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/layer-opacity.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/lite.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/mapserver.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/markerResize.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/markers.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/markersTextLayer.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/mm.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/mvs.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/notile.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/openstreetmap.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/popups.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/proxy.cgi
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/scroll.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/setextent.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/textfile.txt
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/tiger.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/urban.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/ve.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/wfs.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/wms.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/wmst.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/worldwind.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/xhtml.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/yahoo.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/zoomLevels.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/blank.gif
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/close.gif
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/drag-rectangle-off.png
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/drag-rectangle-on.png
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/east-mini.png
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/layer-switcher-maximize.png
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/layer-switcher-minimize.png
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/marker-blue.png
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/marker-gold.png
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/marker-green.png
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/marker.png
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/measuring-stick-off.png
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/measuring-stick-on.png
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/north-mini.png
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/panning-hand-off.png
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/panning-hand-on.png
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/slider.png
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/south-mini.png
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/west-mini.png
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/zoom-minus-mini.png
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/zoom-plus-mini.png
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/zoom-world-mini.png
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/zoombar.png
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Ajax.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/BaseTypes.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/ArgParser.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/KeyboardDefaults.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/LayerSwitcher.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/MouseDefaults.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/MousePosition.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/MouseToolbar.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/OverviewMap.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/PanZoom.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/PanZoomBar.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/Permalink.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/Scale.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Events.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Feature.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Feature/
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Feature/WFS.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Icon.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/Boxes.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/Canvas.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/EventPane.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/FixedZoomLevels.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/GeoRSS.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/Google.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/Grid.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/HTTPRequest.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/Image.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/KaMap.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/MapServer.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/Markers.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/MultiMap.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/Text.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/VirtualEarth.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/WFS.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/WMS.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/WMS/
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/WMS/Untiled.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/WorldWind.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/Yahoo.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Map.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Marker.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Marker/
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Marker/Box.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Popup.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Popup/
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Popup/Anchored.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Popup/AnchoredBubble.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/SingleFile.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Tile.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Tile/
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Tile/Image.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Tile/WFS.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Util.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/Rico/
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/Rico/Color.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/Rico/Corner.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/news.txt
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/readme.txt
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/release-license.txt
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/repository-license.txt
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/README.txt
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/data_Layer_Text_textfile.txt
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/georss.txt
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/list-tests.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/run-tests.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Bounds.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Class.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Control.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Control_LayerSwitcher.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Control_MouseToolbar.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Control_OverviewMap.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Control_PanZoom.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Control_PanZoomBar.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Control_Permalink.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Events.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Feature.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Icon.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_EventPane.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_FixedZoomLevels.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_GeoRSS.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_Google.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_Grid.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_HTTPRequest.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_Image.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_KaMap.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_Markers.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_MultiMap.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_Text.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_WMS.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_LonLat.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Map.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Marker.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Pixel.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Popup.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Size.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Tile.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Tile_Image.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Util.html
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/throws.js
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/theme/
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/theme/default/
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/theme/default/img/
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/theme/default/style.css
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/tools/
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/tools/README.txt
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/tools/jsmin.c
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/tools/jsmin.py
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/tools/mergejs.py
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/tools/release.sh
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/tools/shrinksafe.py
   trunk/PhotoOp/root/static/js/OpenLayers-2.2/tools/toposort.py
   trunk/PhotoOp/root/static/js/src/
   trunk/PhotoOp/root/static/js/src/AdapterRegistry.js
   trunk/PhotoOp/root/static/js/src/Deferred.js
   trunk/PhotoOp/root/static/js/src/animation.js
   trunk/PhotoOp/root/static/js/src/animation/
   trunk/PhotoOp/root/static/js/src/animation/Animation.js
   trunk/PhotoOp/root/static/js/src/animation/AnimationEvent.js
   trunk/PhotoOp/root/static/js/src/animation/AnimationSequence.js
   trunk/PhotoOp/root/static/js/src/animation/Timer.js
   trunk/PhotoOp/root/static/js/src/animation/__package__.js
   trunk/PhotoOp/root/static/js/src/behavior.js
   trunk/PhotoOp/root/static/js/src/bootstrap1.js
   trunk/PhotoOp/root/static/js/src/bootstrap2.js
   trunk/PhotoOp/root/static/js/src/browser_debug.js
   trunk/PhotoOp/root/static/js/src/collections/
   trunk/PhotoOp/root/static/js/src/collections/ArrayList.js
   trunk/PhotoOp/root/static/js/src/collections/BinaryTree.js
   trunk/PhotoOp/root/static/js/src/collections/Collections.js
   trunk/PhotoOp/root/static/js/src/collections/Dictionary.js
   trunk/PhotoOp/root/static/js/src/collections/Graph.js
   trunk/PhotoOp/root/static/js/src/collections/Queue.js
   trunk/PhotoOp/root/static/js/src/collections/Set.js
   trunk/PhotoOp/root/static/js/src/collections/SkipList.js
   trunk/PhotoOp/root/static/js/src/collections/SortedList.js
   trunk/PhotoOp/root/static/js/src/collections/Stack.js
   trunk/PhotoOp/root/static/js/src/collections/__package__.js
   trunk/PhotoOp/root/static/js/src/compat/
   trunk/PhotoOp/root/static/js/src/compat/0.2.2.js
   trunk/PhotoOp/root/static/js/src/crypto.js
   trunk/PhotoOp/root/static/js/src/crypto/
   trunk/PhotoOp/root/static/js/src/crypto/Blowfish.js
   trunk/PhotoOp/root/static/js/src/crypto/LICENSE
   trunk/PhotoOp/root/static/js/src/crypto/MD5.js
   trunk/PhotoOp/root/static/js/src/crypto/Rijndael.js
   trunk/PhotoOp/root/static/js/src/crypto/SHA1.js
   trunk/PhotoOp/root/static/js/src/crypto/SHA256.js
   trunk/PhotoOp/root/static/js/src/crypto/__package__.js
   trunk/PhotoOp/root/static/js/src/data.js
   trunk/PhotoOp/root/static/js/src/data/
   trunk/PhotoOp/root/static/js/src/data/Attribute.js
   trunk/PhotoOp/root/static/js/src/data/Item.js
   trunk/PhotoOp/root/static/js/src/data/Kind.js
   trunk/PhotoOp/root/static/js/src/data/Observable.js
   trunk/PhotoOp/root/static/js/src/data/ResultSet.js
   trunk/PhotoOp/root/static/js/src/data/Type.js
   trunk/PhotoOp/root/static/js/src/data/Value.js
   trunk/PhotoOp/root/static/js/src/data/__package__.js
   trunk/PhotoOp/root/static/js/src/data/format/
   trunk/PhotoOp/root/static/js/src/data/format/Csv.js
   trunk/PhotoOp/root/static/js/src/data/format/Json.js
   trunk/PhotoOp/root/static/js/src/data/provider/
   trunk/PhotoOp/root/static/js/src/data/provider/Base.js
   trunk/PhotoOp/root/static/js/src/data/provider/Delicious.js
   trunk/PhotoOp/root/static/js/src/data/provider/FlatFile.js
   trunk/PhotoOp/root/static/js/src/data/provider/JotSpot.js
   trunk/PhotoOp/root/static/js/src/data/provider/MySql.js
   trunk/PhotoOp/root/static/js/src/data/to_do.txt
   trunk/PhotoOp/root/static/js/src/date.js
   trunk/PhotoOp/root/static/js/src/debug.js
   trunk/PhotoOp/root/static/js/src/debug/
   trunk/PhotoOp/root/static/js/src/debug/Firebug.js
   trunk/PhotoOp/root/static/js/src/debug/arrow_hide.gif
   trunk/PhotoOp/root/static/js/src/debug/arrow_show.gif
   trunk/PhotoOp/root/static/js/src/debug/deep.html
   trunk/PhotoOp/root/static/js/src/debug/spacer.gif
   trunk/PhotoOp/root/static/js/src/dnd/
   trunk/PhotoOp/root/static/js/src/dnd/DragAndDrop.js
   trunk/PhotoOp/root/static/js/src/dnd/HtmlDragAndDrop.js
   trunk/PhotoOp/root/static/js/src/dnd/HtmlDragManager.js
   trunk/PhotoOp/root/static/js/src/dnd/HtmlDragMove.js
   trunk/PhotoOp/root/static/js/src/dnd/Sortable.js
   trunk/PhotoOp/root/static/js/src/dnd/TreeDragAndDrop.js
   trunk/PhotoOp/root/static/js/src/dnd/__package__.js
   trunk/PhotoOp/root/static/js/src/doc.js
   trunk/PhotoOp/root/static/js/src/dom.js
   trunk/PhotoOp/root/static/js/src/event.js
   trunk/PhotoOp/root/static/js/src/event/
   trunk/PhotoOp/root/static/js/src/event/__package__.js
   trunk/PhotoOp/root/static/js/src/event/browser.js
   trunk/PhotoOp/root/static/js/src/event/topic.js
   trunk/PhotoOp/root/static/js/src/experimental.js
   trunk/PhotoOp/root/static/js/src/flash.js
   trunk/PhotoOp/root/static/js/src/flash/
   trunk/PhotoOp/root/static/js/src/flash/flash6/
   trunk/PhotoOp/root/static/js/src/flash/flash6/DojoExternalInterface.as
   trunk/PhotoOp/root/static/js/src/flash/flash6/flash6_gateway.fla
   trunk/PhotoOp/root/static/js/src/flash/flash8/
   trunk/PhotoOp/root/static/js/src/flash/flash8/DojoExternalInterface.as
   trunk/PhotoOp/root/static/js/src/flash/flash8/ExpressInstall.as
   trunk/PhotoOp/root/static/js/src/fx/
   trunk/PhotoOp/root/static/js/src/fx/__package__.js
   trunk/PhotoOp/root/static/js/src/fx/html.js
   trunk/PhotoOp/root/static/js/src/fx/svg.js
   trunk/PhotoOp/root/static/js/src/graphics/
   trunk/PhotoOp/root/static/js/src/graphics/Colorspace.js
   trunk/PhotoOp/root/static/js/src/graphics/__package__.js
   trunk/PhotoOp/root/static/js/src/graphics/color.js
   trunk/PhotoOp/root/static/js/src/graphics/color/
   trunk/PhotoOp/root/static/js/src/graphics/color/hsl.js
   trunk/PhotoOp/root/static/js/src/graphics/color/hsv.js
   trunk/PhotoOp/root/static/js/src/hostenv_adobesvg.js
   trunk/PhotoOp/root/static/js/src/hostenv_browser.js
   trunk/PhotoOp/root/static/js/src/hostenv_dashboard.js
   trunk/PhotoOp/root/static/js/src/hostenv_jsc.js
   trunk/PhotoOp/root/static/js/src/hostenv_rhino.js
   trunk/PhotoOp/root/static/js/src/hostenv_spidermonkey.js
   trunk/PhotoOp/root/static/js/src/hostenv_svg.js
   trunk/PhotoOp/root/static/js/src/hostenv_wsh.js
   trunk/PhotoOp/root/static/js/src/html.js
   trunk/PhotoOp/root/static/js/src/html/
   trunk/PhotoOp/root/static/js/src/html/__package__.js
   trunk/PhotoOp/root/static/js/src/html/extras.js
   trunk/PhotoOp/root/static/js/src/html/images/
   trunk/PhotoOp/root/static/js/src/html/images/shadowB.png
   trunk/PhotoOp/root/static/js/src/html/images/shadowBL.png
   trunk/PhotoOp/root/static/js/src/html/images/shadowBR.png
   trunk/PhotoOp/root/static/js/src/html/images/shadowL.png
   trunk/PhotoOp/root/static/js/src/html/images/shadowR.png
   trunk/PhotoOp/root/static/js/src/html/images/shadowT.png
   trunk/PhotoOp/root/static/js/src/html/images/shadowTL.png
   trunk/PhotoOp/root/static/js/src/html/images/shadowTR..png
   trunk/PhotoOp/root/static/js/src/html/images/shadowTR.png
   trunk/PhotoOp/root/static/js/src/html/layout.js
   trunk/PhotoOp/root/static/js/src/html/shadow.js
   trunk/PhotoOp/root/static/js/src/i18n/
   trunk/PhotoOp/root/static/js/src/i18n/common.js
   trunk/PhotoOp/root/static/js/src/iCalendar.js
   trunk/PhotoOp/root/static/js/src/io.js
   trunk/PhotoOp/root/static/js/src/io/
   trunk/PhotoOp/root/static/js/src/io/BrowserIO.js
   trunk/PhotoOp/root/static/js/src/io/IframeIO.js
   trunk/PhotoOp/root/static/js/src/io/RepubsubIO.js
   trunk/PhotoOp/root/static/js/src/io/RhinoIO.js
   trunk/PhotoOp/root/static/js/src/io/ScriptSrcIO.js
   trunk/PhotoOp/root/static/js/src/io/ShortBusIO.js
   trunk/PhotoOp/root/static/js/src/io/ShortBusInit.html
   trunk/PhotoOp/root/static/js/src/io/__package__.js
   trunk/PhotoOp/root/static/js/src/io/cookie.js
   trunk/PhotoOp/root/static/js/src/io/cookies.js
   trunk/PhotoOp/root/static/js/src/json.js
   trunk/PhotoOp/root/static/js/src/lang.js
   trunk/PhotoOp/root/static/js/src/lang/
   trunk/PhotoOp/root/static/js/src/lang/Lang.js
   trunk/PhotoOp/root/static/js/src/lang/__package__.js
   trunk/PhotoOp/root/static/js/src/lang/array.js
   trunk/PhotoOp/root/static/js/src/lang/assert.js
   trunk/PhotoOp/root/static/js/src/lang/common.js
   trunk/PhotoOp/root/static/js/src/lang/declare.js
   trunk/PhotoOp/root/static/js/src/lang/extras.js
   trunk/PhotoOp/root/static/js/src/lang/func.js
   trunk/PhotoOp/root/static/js/src/lang/repr.js
   trunk/PhotoOp/root/static/js/src/lang/type.js
   trunk/PhotoOp/root/static/js/src/lfx/
   trunk/PhotoOp/root/static/js/src/lfx/Animation.js
   trunk/PhotoOp/root/static/js/src/lfx/__package__.js
   trunk/PhotoOp/root/static/js/src/lfx/extras.js
   trunk/PhotoOp/root/static/js/src/lfx/html.js
   trunk/PhotoOp/root/static/js/src/lfx/toggle.js
   trunk/PhotoOp/root/static/js/src/loader.js
   trunk/PhotoOp/root/static/js/src/loader_xd.js
   trunk/PhotoOp/root/static/js/src/logging/
   trunk/PhotoOp/root/static/js/src/logging/Logger.js
   trunk/PhotoOp/root/static/js/src/logging/__package__.js
   trunk/PhotoOp/root/static/js/src/math.js
   trunk/PhotoOp/root/static/js/src/math/
   trunk/PhotoOp/root/static/js/src/math/Math.js
   trunk/PhotoOp/root/static/js/src/math/__package__.js
   trunk/PhotoOp/root/static/js/src/math/curves.js
   trunk/PhotoOp/root/static/js/src/math/matrix.js
   trunk/PhotoOp/root/static/js/src/math/points.js
   trunk/PhotoOp/root/static/js/src/profile.js
   trunk/PhotoOp/root/static/js/src/reflect/
   trunk/PhotoOp/root/static/js/src/reflect/__package__.js
   trunk/PhotoOp/root/static/js/src/reflect/reflection.js
   trunk/PhotoOp/root/static/js/src/regexp.js
   trunk/PhotoOp/root/static/js/src/rpc/
   trunk/PhotoOp/root/static/js/src/rpc/Deferred.js
   trunk/PhotoOp/root/static/js/src/rpc/JotService.js
   trunk/PhotoOp/root/static/js/src/rpc/JsonService.js
   trunk/PhotoOp/root/static/js/src/rpc/RpcService.js
   trunk/PhotoOp/root/static/js/src/rpc/YahooService.js
   trunk/PhotoOp/root/static/js/src/rpc/__package__.js
   trunk/PhotoOp/root/static/js/src/rpc/yahoo.smd
   trunk/PhotoOp/root/static/js/src/selection/
   trunk/PhotoOp/root/static/js/src/selection/Selection.js
   trunk/PhotoOp/root/static/js/src/storage.js
   trunk/PhotoOp/root/static/js/src/storage/
   trunk/PhotoOp/root/static/js/src/storage/Storage.as
   trunk/PhotoOp/root/static/js/src/storage/__package__.js
   trunk/PhotoOp/root/static/js/src/storage/browser.js
   trunk/PhotoOp/root/static/js/src/storage/dashboard.js
   trunk/PhotoOp/root/static/js/src/storage/storage_dialog.fla
   trunk/PhotoOp/root/static/js/src/string.js
   trunk/PhotoOp/root/static/js/src/string/
   trunk/PhotoOp/root/static/js/src/string/Builder.js
   trunk/PhotoOp/root/static/js/src/string/__package__.js
   trunk/PhotoOp/root/static/js/src/string/common.js
   trunk/PhotoOp/root/static/js/src/string/extras.js
   trunk/PhotoOp/root/static/js/src/style.js
   trunk/PhotoOp/root/static/js/src/svg.js
   trunk/PhotoOp/root/static/js/src/text/
   trunk/PhotoOp/root/static/js/src/text/Builder.js
   trunk/PhotoOp/root/static/js/src/text/String.js
   trunk/PhotoOp/root/static/js/src/text/Text.js
   trunk/PhotoOp/root/static/js/src/text/__package__.js
   trunk/PhotoOp/root/static/js/src/text/textDirectory.js
   trunk/PhotoOp/root/static/js/src/undo/
   trunk/PhotoOp/root/static/js/src/undo/Manager.js
   trunk/PhotoOp/root/static/js/src/undo/__package__.js
   trunk/PhotoOp/root/static/js/src/undo/browser.js
   trunk/PhotoOp/root/static/js/src/uri/
   trunk/PhotoOp/root/static/js/src/uri/Uri.js
   trunk/PhotoOp/root/static/js/src/uri/__package__.js
   trunk/PhotoOp/root/static/js/src/uuid/
   trunk/PhotoOp/root/static/js/src/uuid/LightweightGenerator.js
   trunk/PhotoOp/root/static/js/src/uuid/NameBasedGenerator.js
   trunk/PhotoOp/root/static/js/src/uuid/NilGenerator.js
   trunk/PhotoOp/root/static/js/src/uuid/RandomGenerator.js
   trunk/PhotoOp/root/static/js/src/uuid/TimeBasedGenerator.js
   trunk/PhotoOp/root/static/js/src/uuid/Uuid.js
   trunk/PhotoOp/root/static/js/src/uuid/__package__.js
   trunk/PhotoOp/root/static/js/src/validate.js
   trunk/PhotoOp/root/static/js/src/validate/
   trunk/PhotoOp/root/static/js/src/validate/__package__.js
   trunk/PhotoOp/root/static/js/src/validate/check.js
   trunk/PhotoOp/root/static/js/src/validate/common.js
   trunk/PhotoOp/root/static/js/src/validate/datetime.js
   trunk/PhotoOp/root/static/js/src/validate/de.js
   trunk/PhotoOp/root/static/js/src/validate/jp.js
   trunk/PhotoOp/root/static/js/src/validate/us.js
   trunk/PhotoOp/root/static/js/src/validate/web.js
   trunk/PhotoOp/root/static/js/src/widget/
   trunk/PhotoOp/root/static/js/src/widget/AccordionContainer.js
   trunk/PhotoOp/root/static/js/src/widget/AccordionPane.js
   trunk/PhotoOp/root/static/js/src/widget/AnimatedPng.js
   trunk/PhotoOp/root/static/js/src/widget/Button.js
   trunk/PhotoOp/root/static/js/src/widget/Button2.js
   trunk/PhotoOp/root/static/js/src/widget/Chart.js
   trunk/PhotoOp/root/static/js/src/widget/Checkbox.js
   trunk/PhotoOp/root/static/js/src/widget/CiviCrmDatePicker.js
   trunk/PhotoOp/root/static/js/src/widget/ColorPalette.js
   trunk/PhotoOp/root/static/js/src/widget/ComboBox.js
   trunk/PhotoOp/root/static/js/src/widget/ContentPane.js
   trunk/PhotoOp/root/static/js/src/widget/ContextMenu.js
   trunk/PhotoOp/root/static/js/src/widget/DatePicker.js
   trunk/PhotoOp/root/static/js/src/widget/DebugConsole.js
   trunk/PhotoOp/root/static/js/src/widget/DemoEngine.js
   trunk/PhotoOp/root/static/js/src/widget/Dialog.js
   trunk/PhotoOp/root/static/js/src/widget/DocPane.js
   trunk/PhotoOp/root/static/js/src/widget/DomWidget.js
   trunk/PhotoOp/root/static/js/src/widget/DropdownButton.js
   trunk/PhotoOp/root/static/js/src/widget/DropdownContainer.js
   trunk/PhotoOp/root/static/js/src/widget/DropdownDatePicker.js
   trunk/PhotoOp/root/static/js/src/widget/Editor.js
   trunk/PhotoOp/root/static/js/src/widget/Editor2.js
   trunk/PhotoOp/root/static/js/src/widget/Editor2Toolbar.js
   trunk/PhotoOp/root/static/js/src/widget/FisheyeList.js
   trunk/PhotoOp/root/static/js/src/widget/FloatingPane.js
   trunk/PhotoOp/root/static/js/src/widget/GoogleMap.js
   trunk/PhotoOp/root/static/js/src/widget/HslColorPicker.js
   trunk/PhotoOp/root/static/js/src/widget/HtmlWidget.js
   trunk/PhotoOp/root/static/js/src/widget/InlineEditBox.js
   trunk/PhotoOp/root/static/js/src/widget/LayoutContainer.js
   trunk/PhotoOp/root/static/js/src/widget/LinkPane.js
   trunk/PhotoOp/root/static/js/src/widget/Manager.js
   trunk/PhotoOp/root/static/js/src/widget/Menu.js
   trunk/PhotoOp/root/static/js/src/widget/Menu2.js
   trunk/PhotoOp/root/static/js/src/widget/MenuItem.js
   trunk/PhotoOp/root/static/js/src/widget/MonthlyCalendar.js
   trunk/PhotoOp/root/static/js/src/widget/Parse.js
   trunk/PhotoOp/root/static/js/src/widget/PopUpButton.js
   trunk/PhotoOp/root/static/js/src/widget/ResizableTextarea.js
   trunk/PhotoOp/root/static/js/src/widget/ResizeHandle.js
   trunk/PhotoOp/root/static/js/src/widget/RichText.js
   trunk/PhotoOp/root/static/js/src/widget/Rounded.js
   trunk/PhotoOp/root/static/js/src/widget/Select.js
   trunk/PhotoOp/root/static/js/src/widget/Show.js
   trunk/PhotoOp/root/static/js/src/widget/ShowAction.js
   trunk/PhotoOp/root/static/js/src/widget/ShowSlide.js
   trunk/PhotoOp/root/static/js/src/widget/SimpleDropdownButtons.js
   trunk/PhotoOp/root/static/js/src/widget/SlideShow.js
   trunk/PhotoOp/root/static/js/src/widget/SortableTable.js
   trunk/PhotoOp/root/static/js/src/widget/Spinner.js
   trunk/PhotoOp/root/static/js/src/widget/SplitContainer.js
   trunk/PhotoOp/root/static/js/src/widget/SvgButton.js
   trunk/PhotoOp/root/static/js/src/widget/SvgWidget.js
   trunk/PhotoOp/root/static/js/src/widget/SwtWidget.js
   trunk/PhotoOp/root/static/js/src/widget/TabContainer.js
   trunk/PhotoOp/root/static/js/src/widget/TaskBar.js
   trunk/PhotoOp/root/static/js/src/widget/TimePicker.js
   trunk/PhotoOp/root/static/js/src/widget/TitlePane.js
   trunk/PhotoOp/root/static/js/src/widget/Toggler.js
   trunk/PhotoOp/root/static/js/src/widget/Toolbar.js
   trunk/PhotoOp/root/static/js/src/widget/Tooltip.js
   trunk/PhotoOp/root/static/js/src/widget/Tree.js
   trunk/PhotoOp/root/static/js/src/widget/TreeBasicController.js
   trunk/PhotoOp/root/static/js/src/widget/TreeContextMenu.js
   trunk/PhotoOp/root/static/js/src/widget/TreeControllerExtension.js
   trunk/PhotoOp/root/static/js/src/widget/TreeLoadingController.js
   trunk/PhotoOp/root/static/js/src/widget/TreeNode.js
   trunk/PhotoOp/root/static/js/src/widget/TreeRPCController.js
   trunk/PhotoOp/root/static/js/src/widget/TreeSelector.js
   trunk/PhotoOp/root/static/js/src/widget/Widget.js
   trunk/PhotoOp/root/static/js/src/widget/Wizard.js
   trunk/PhotoOp/root/static/js/src/widget/YahooMap.js
   trunk/PhotoOp/root/static/js/src/widget/__package__.js
   trunk/PhotoOp/root/static/js/src/widget/demoEngine/
   trunk/PhotoOp/root/static/js/src/widget/demoEngine/DemoContainer.js
   trunk/PhotoOp/root/static/js/src/widget/demoEngine/DemoItem.js
   trunk/PhotoOp/root/static/js/src/widget/demoEngine/DemoNavigator.js
   trunk/PhotoOp/root/static/js/src/widget/demoEngine/DemoPane.js
   trunk/PhotoOp/root/static/js/src/widget/demoEngine/SourcePane.js
   trunk/PhotoOp/root/static/js/src/widget/demoEngine/__package__.js
   trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/
   trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/DemoContainer.css
   trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/DemoContainer.html
   trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/DemoItem.css
   trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/DemoItem.html
   trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/DemoNavigator.css
   trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/DemoNavigator.html
   trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/DemoPane.css
   trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/DemoPane.html
   trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/SourcePane.css
   trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/SourcePane.html
   trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/general.css
   trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/images/
   trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/images/test_thumb.gif
   trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/images/viewDemo.png
   trunk/PhotoOp/root/static/js/src/widget/html/
   trunk/PhotoOp/root/static/js/src/widget/html/AccordionPane.js
   trunk/PhotoOp/root/static/js/src/widget/html/Button.js
   trunk/PhotoOp/root/static/js/src/widget/html/Button2.js
   trunk/PhotoOp/root/static/js/src/widget/html/Checkbox.js
   trunk/PhotoOp/root/static/js/src/widget/html/ComboBox.js
   trunk/PhotoOp/root/static/js/src/widget/html/ContentPane.js
   trunk/PhotoOp/root/static/js/src/widget/html/ContextMenu.js
   trunk/PhotoOp/root/static/js/src/widget/html/DatePicker.js
   trunk/PhotoOp/root/static/js/src/widget/html/DebugConsole.js
   trunk/PhotoOp/root/static/js/src/widget/html/DemoEngine.js
   trunk/PhotoOp/root/static/js/src/widget/html/DocPane.js
   trunk/PhotoOp/root/static/js/src/widget/html/DropdownButton.js
   trunk/PhotoOp/root/static/js/src/widget/html/GoogleMap.js
   trunk/PhotoOp/root/static/js/src/widget/html/LinkPane.js
   trunk/PhotoOp/root/static/js/src/widget/html/Menu.js
   trunk/PhotoOp/root/static/js/src/widget/html/MenuItem.js
   trunk/PhotoOp/root/static/js/src/widget/html/MonthlyCalendar.js
   trunk/PhotoOp/root/static/js/src/widget/html/Show.js
   trunk/PhotoOp/root/static/js/src/widget/html/ShowAction.js
   trunk/PhotoOp/root/static/js/src/widget/html/ShowSlide.js
   trunk/PhotoOp/root/static/js/src/widget/html/Slider.js
   trunk/PhotoOp/root/static/js/src/widget/html/SortableTable.js
   trunk/PhotoOp/root/static/js/src/widget/html/Spinner.js
   trunk/PhotoOp/root/static/js/src/widget/html/TaskBar.js
   trunk/PhotoOp/root/static/js/src/widget/html/TimePicker.js
   trunk/PhotoOp/root/static/js/src/widget/html/TitlePane.js
   trunk/PhotoOp/root/static/js/src/widget/html/Tooltip.js
   trunk/PhotoOp/root/static/js/src/widget/html/YahooMap.js
   trunk/PhotoOp/root/static/js/src/widget/html/stabile.js
   trunk/PhotoOp/root/static/js/src/widget/svg/
   trunk/PhotoOp/root/static/js/src/widget/svg/Chart.js
   trunk/PhotoOp/root/static/js/src/widget/svg/HslColorPicker.js
   trunk/PhotoOp/root/static/js/src/widget/templates/
   trunk/PhotoOp/root/static/js/src/widget/templates/DemoEngine.html
   trunk/PhotoOp/root/static/js/src/widget/templates/HslColorPicker.svg
   trunk/PhotoOp/root/static/js/src/widget/templates/HtmlButtonTemplate.css
   trunk/PhotoOp/root/static/js/src/widget/templates/HtmlButtonTemplate.html
   trunk/PhotoOp/root/static/js/src/widget/templates/HtmlCheckBox.html
   trunk/PhotoOp/root/static/js/src/widget/templates/HtmlCiviCrmDatePicker.html
   trunk/PhotoOp/root/static/js/src/widget/templates/HtmlComboBox.css
   trunk/PhotoOp/root/static/js/src/widget/templates/HtmlComboBox.html
   trunk/PhotoOp/root/static/js/src/widget/templates/HtmlComboButtonTemplate.html
   trunk/PhotoOp/root/static/js/src/widget/templates/HtmlContextMenuTemplate.html
   trunk/PhotoOp/root/static/js/src/widget/templates/HtmlDatePicker.css
   trunk/PhotoOp/root/static/js/src/widget/templates/HtmlDatePicker.html
   trunk/PhotoOp/root/static/js/src/widget/templates/HtmlDialog.html
   trunk/PhotoOp/root/static/js/src/widget/templates/HtmlDocPane.css
   trunk/PhotoOp/root/static/js/src/widget/templates/HtmlDocPane.html
   trunk/PhotoOp/root/static/js/src/widget/templates/HtmlDropDownButtonTemplate.html
   trunk/PhotoOp/root/static/js/src/widget/templates/HtmlEditorToolbar.css
   trunk/PhotoOp/root/static/js/src/widget/templates/HtmlEditorToolbar.html
   trunk/PhotoOp/root/static/js/src/widget/templates/HtmlEditorToolbarOneline.html
   trunk/PhotoOp/root/static/js/src/widget/templates/HtmlFisheyeList.css
   trunk/PhotoOp/root/static/js/src/widget/templates/HtmlFloatingPane.css
   trunk/PhotoOp/root/static/js/src/widget/templates/HtmlFloatingPane.html
   trunk/PhotoOp/root/static/js/src/widget/templates/HtmlInlineEditBox.css
   trunk/PhotoOp/root/static/js/src/widget/templates/HtmlInlineEditBox.html
   trunk/PhotoOp/root/static/js/src/widget/templates/HtmlMenu2.css
   trunk/PhotoOp/root/static/js/src/widget/templates/HtmlMenuItemTemplate.html
   trunk/PhotoOp/root/static/js/src/widget/templates/HtmlMonthlyCalendar.css
   trunk/PhotoOp/root/static/js/src/widget/templates/HtmlMonthlyCalendar.html
   trunk/PhotoOp/root/static/js/src/widget/templates/HtmlResizableTextarea.css
   trunk/PhotoOp/root/static/js/src/widget/templates/HtmlResizableTextarea.html
   trunk/PhotoOp/root/static/js/src/widget/templates/HtmlResizeHandle.css
   trunk/PhotoOp/root/static/js/src/widget/templates/HtmlShow.css
   trunk/PhotoOp/root/static/js/src/widget/templates/HtmlShow.html
   trunk/PhotoOp/root/static/js/src/widget/templates/HtmlShowSlide.css
   trunk/PhotoOp/root/static/js/src/widget/templates/HtmlShowSlide.html
   trunk/PhotoOp/root/static/js/src/widget/templates/HtmlSimpleDropdownButtons.css
   trunk/PhotoOp/root/static/js/src/widget/templates/HtmlSlideShow.css
   trunk/PhotoOp/root/static/js/src/widget/templates/HtmlSlideShow.html
   trunk/PhotoOp/root/static/js/src/widget/templates/HtmlSlider.css
   trunk/PhotoOp/root/static/js/src/widget/templates/HtmlSlider.html
   trunk/PhotoOp/root/static/js/src/widget/templates/HtmlSliderHorizontal.html
   trunk/PhotoOp/root/static/js/src/widget/templates/HtmlSliderVertical.html
   trunk/PhotoOp/root/static/js/src/widget/templates/HtmlSpinner.css
   trunk/PhotoOp/root/static/js/src/widget/templates/HtmlSpinner.html
   trunk/PhotoOp/root/static/js/src/widget/templates/HtmlSplitContainer.css
   trunk/PhotoOp/root/static/js/src/widget/templates/HtmlTabContainer.css
   trunk/PhotoOp/root/static/js/src/widget/templates/HtmlTabContainer.html
   trunk/PhotoOp/root/static/js/src/widget/templates/HtmlTaskBar.css
   trunk/PhotoOp/root/static/js/src/widget/templates/HtmlTaskBarItemTemplate.html
   trunk/PhotoOp/root/static/js/src/widget/templates/HtmlTimePicker.css
   trunk/PhotoOp/root/static/js/src/widget/templates/HtmlTimePicker.html
   trunk/PhotoOp/root/static/js/src/widget/templates/HtmlToolbar.css
   trunk/PhotoOp/root/static/js/src/widget/templates/HtmlTooltipTemplate.css
   trunk/PhotoOp/root/static/js/src/widget/templates/HtmlTooltipTemplate.html
   trunk/PhotoOp/root/static/js/src/widget/templates/Menu.css
   trunk/PhotoOp/root/static/js/src/widget/templates/PopUpButton.css
   trunk/PhotoOp/root/static/js/src/widget/templates/TitlePane.html
   trunk/PhotoOp/root/static/js/src/widget/templates/Tree.css
   trunk/PhotoOp/root/static/js/src/widget/templates/Wizard.css
   trunk/PhotoOp/root/static/js/src/widget/templates/Wizard.html
   trunk/PhotoOp/root/static/js/src/widget/templates/buttons/
   trunk/PhotoOp/root/static/js/src/widget/templates/buttons/-.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/buttons/aggregate.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/buttons/backcolor.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/buttons/bg-fade.png
   trunk/PhotoOp/root/static/js/src/widget/templates/buttons/bold.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/buttons/cancel.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/buttons/copy.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/buttons/createlink.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/buttons/cut.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/buttons/delete.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/buttons/forecolor.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/buttons/hilitecolor.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/buttons/indent.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/buttons/inserthorizontalrule.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/buttons/insertimage.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/buttons/insertorderedlist.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/buttons/inserttable.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/buttons/insertunorderedlist.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/buttons/italic.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/buttons/justifycenter.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/buttons/justifyfull.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/buttons/justifyleft.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/buttons/justifyright.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/buttons/left_to_right.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/buttons/list_bullet_indent.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/buttons/list_bullet_outdent.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/buttons/list_num_indent.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/buttons/list_num_outdent.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/buttons/outdent.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/buttons/paste.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/buttons/redo.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/buttons/removeformat.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/buttons/right_to_left.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/buttons/save.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/buttons/space.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/buttons/strikethrough.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/buttons/subscript.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/buttons/superscript.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/buttons/underline.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/buttons/undo.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/buttons/wikiword.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/check_disabled_off.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/check_disabled_on.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/check_hover_off.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/check_hover_on.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/check_off.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/check_on.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/decrementMonth.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/decrementWeek.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/grabCorner.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/
   trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/
   trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/Tree.css
   trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/blank.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/closed.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/document.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/minus.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/plus.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/transparent.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/treenode_blank.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/treenode_child.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/treenode_expand_minus.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/treenode_expand_plus.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/treenode_grid_c.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/treenode_grid_l.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/treenode_grid_p.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/treenode_grid_t.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/treenode_grid_v.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/treenode_grid_x.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/treenode_grid_y.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/treenode_grid_z.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/treenode_loading.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/treenode_loading.jpg
   trunk/PhotoOp/root/static/js/src/widget/templates/images/bdYearBg.1.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/bdYearBg.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/blank.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/combo_box_arrow.png
   trunk/PhotoOp/root/static/js/src/widget/templates/images/dateIcon.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/decrementMonth.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/decrementMonth.png
   trunk/PhotoOp/root/static/js/src/widget/templates/images/dpBg.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/dpCurveBL.png
   trunk/PhotoOp/root/static/js/src/widget/templates/images/dpCurveBR.png
   trunk/PhotoOp/root/static/js/src/widget/templates/images/dpCurveTL.png
   trunk/PhotoOp/root/static/js/src/widget/templates/images/dpCurveTR.png
   trunk/PhotoOp/root/static/js/src/widget/templates/images/dpHorizLine.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/dpHorizLineFoot.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/dpMonthBg.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/dpMonthBg.png
   trunk/PhotoOp/root/static/js/src/widget/templates/images/dpVertLine.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/dpYearBg.png
   trunk/PhotoOp/root/static/js/src/widget/templates/images/dropdownButtonsArrow-disabled.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/dropdownButtonsArrow.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/floatingPaneClose.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/floatingPaneMaximize.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/floatingPaneMinimize.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/floatingPaneRestore.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/hue.png
   trunk/PhotoOp/root/static/js/src/widget/templates/images/incrementMonth.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/incrementMonth.png
   trunk/PhotoOp/root/static/js/src/widget/templates/images/no.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/no.svg
   trunk/PhotoOp/root/static/js/src/widget/templates/images/scBackground.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/slider-bg-vert.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/slider-bg.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/slider-button-horz.png
   trunk/PhotoOp/root/static/js/src/widget/templates/images/slider-button-vert.png
   trunk/PhotoOp/root/static/js/src/widget/templates/images/slider-button.png
   trunk/PhotoOp/root/static/js/src/widget/templates/images/soriaActive-c.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/soriaActive-l.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/soriaActive-r.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/soriaButton-c.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/soriaButton-l.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/soriaButton-r.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/soriaDisabled-c.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/soriaDisabled-l.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/soriaDisabled-r.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/soriaPressed-c.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/soriaPressed-l.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/soriaPressed-r.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/spinnerDecrement.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/spinnerIncrement.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/submenu_off.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/submenu_on.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/tab_bot_left.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/tab_bot_left_curr.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/tab_bot_right.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/tab_bot_right_curr.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/tab_close.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/tab_close_h.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/tab_left.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/tab_left_r.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/tab_left_r_curr.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/tab_right.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/tab_right_r.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/tab_right_r_curr.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/tab_top_left.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/tab_top_right.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/toolbar-bg.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/transparent.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/treenode_blank.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/treenode_child.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/treenode_expand_minus.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/treenode_expand_plus.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/treenode_grid_c.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/treenode_grid_l.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/treenode_grid_p.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/treenode_grid_t.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/treenode_grid_v.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/treenode_grid_x.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/treenode_grid_y.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/treenode_grid_z.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/treenode_node.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/verticalbar.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/images/whiteDownArrow.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/incrementMonth.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/incrementWeek.gif
   trunk/PhotoOp/root/static/js/src/widget/templates/richtextframe.html
   trunk/PhotoOp/root/static/js/src/widget/validate.js
   trunk/PhotoOp/root/static/js/src/widget/vml/
   trunk/PhotoOp/root/static/js/src/widget/vml/Chart.js
   trunk/PhotoOp/root/static/js/src/xml/
   trunk/PhotoOp/root/static/js/src/xml/Parse.js
   trunk/PhotoOp/root/static/js/src/xml/__package__.js
   trunk/PhotoOp/root/static/js/src/xml/domUtil.js
   trunk/PhotoOp/root/static/js/src/xml/htmlUtil.js
   trunk/PhotoOp/root/static/js/src/xml/svgUtil.js
Modified:
   trunk/PhotoOp/bin/sort_pics.pl
   trunk/PhotoOp/lib/PhotoOp.pm
   trunk/PhotoOp/lib/PhotoOp/Controller/Root.pm
   trunk/PhotoOp/lib/PhotoOp/Model/Image.pm
   trunk/PhotoOp/root/editfile.tt
   trunk/PhotoOp/root/header.tt
   trunk/PhotoOp/root/openlayers.tt
Log:
Update to latest version (mass dump)


Added: trunk/PhotoOp/bin/copy_pic.pl
===================================================================
--- trunk/PhotoOp/bin/copy_pic.pl	                        (rev 0)
+++ trunk/PhotoOp/bin/copy_pic.pl	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,117 @@
+#!/usr/bin/perl
+use warnings;
+use strict;
+
+use Getopt::Long;
+$|++; # We exec below, so pipes piping hot, please.
+use DirHandle;
+use File::Copy;
+
+my $sourcedir = '';
+my $mountdev = 'true';
+GetOptions ('source:s' => \$sourcedir,
+            'mount:s' => \$mountdev );
+
+my $mountdir  = '/media/sm';
+my $devicedir = $mountdir . '/imolym';
+my $outdir    = '/mnt/winprog/Pics/';
+my $oldscript = '/home/castaway/perl/perlprogs/copyPICold.perl';
+
+$sourcedir ||= $devicedir;
+
+# Mounts the given dir.
+# Arguments:
+#  Dir to pass to mount.  (Does not support more then a path -- IE 
+#  everything else must be in fstab.)
+# Returns: 
+#  1 on success...
+#  2 if was already mounted...
+#  undef if failure
+sub domount {
+  my $path=shift;
+  my $mountoutput=`mount $path`;
+  my $mountreturn=$?;
+  
+#  print "Output: $mountoutput";
+  print "Return: $mountreturn";
+
+  if ($mountreturn == 0) {
+	return 1;
+  }
+  return 1 if ($mountoutput =~ /not a valid/i);
+  # Already mounted, or mountpoint busy.
+  if ($mountreturn == 32) {
+	return 2;
+  }
+  if ($mountreturn == 8192) {
+	return 2;
+  }
+  print "Mounting $path failed (\$?=$?): $mountoutput\n";
+  return undef;
+}
+
+# Finds the maximum picture number in use.
+# Arguments: Path, regex matching pictures and capturing a number.
+# (Will return 1 if no pictures found, or die on error.)
+sub getmaxpicnum {
+  my ($path, $regex) = @_;
+  my $maxnum = 1;
+
+  my $dir = DirHandle->new($path) or die "Can't opendir $path";
+  while (defined($_=$dir->read)) {
+#	print "$_: ";
+	next unless (/$regex/);
+	my $num = $1+0;
+#	print "$num\n";
+	if ($maxnum<$num) {
+	  $maxnum=$num;
+	}
+  }
+  return $maxnum;
+}
+
+my $mount = 2;
+if($mountdev eq 'true')
+{
+    print "Attempting to mount $mountdir: ";
+    $mount = domount($mountdir);
+    if (!$mount) {
+        print "Mounting failed, trying old method.\n";
+
+        # Doesn't flush, etc!
+        exec $oldscript;
+    } else {
+        print "Done.\n";
+    }
+}
+
+# Note that this is the max currently existing pic num, should start
+# output at this +1.
+my $picnum = getmaxpicnum($outdir, qr/^PIC(\d+)\.jpg$/i);
+
+print "Starting at picture number $picnum.\n";
+
+my $picsdir = DirHandle->new($sourcedir) or die "Couldn't opendir $sourcedir: $!";
+my @pics = sort $picsdir->read;
+foreach (@pics) {
+  next if ($_ eq '.');
+  next if ($_ eq '..');
+  $_ = $sourcedir . '/' . $_;
+  $picnum++;
+  my $outpath = sprintf("$outdir/PIC%05d.jpg", $picnum);
+  print "$_ => $outpath\n";
+  File::Copy::syscopy($_, $outpath) or die "Couldn't copy file: $!";
+}
+
+# Don't umount if was already mounted
+if ($mount != 2) {
+  print "umounting $mountdir: ";
+  my $umountout = `umount $mountdir`;
+  if ($umountout) {
+	print $umountout;
+  } else {
+	print "Done.\n";
+  }
+} else {
+  print "Was already mounted, so not umounting.\n";
+}


Property changes on: trunk/PhotoOp/bin/copy_pic.pl
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/bin/create_user.pl
===================================================================
--- trunk/PhotoOp/bin/create_user.pl	                        (rev 0)
+++ trunk/PhotoOp/bin/create_user.pl	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,54 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Getopt::Long;
+use Authen::Htpasswd;
+
+my %options = (
+               filename => '',
+               user => '',
+               password => '',
+               roles => '',
+               );
+
+my $res = GetOptions( 'file=s' => \$options{filename},
+                      'user=s' => \$options{user},
+                      'password=s' => \$options{password},
+                      'roles:s' => \$options{roles},
+                      );
+
+if(!$res || !$options{filename})
+{
+    print STDERR "Usage: $0 --file='<filename>'
+                    --user='<username>'
+                    --password='<password>'
+                    [ --roles=admin,viewer ]\n"; 
+
+    exit 0;
+}
+
+if(!-e $options{filename})
+{
+    die "$options{filename} doesn't exist!";
+}
+
+my $pwfile = Authen::Htpasswd->new($options{filename});
+
+my $user = $pwfile->lookup_user($options{user});
+if(defined $user)
+{
+    $user->set(password => $options{password},
+               extra_info => [ split(/,/, $options{roles}) ],
+               );
+}
+else
+{
+    $pwfile->add_user($options{user},
+                      $options{password},
+                      split(/,/, $options{roles}),
+                      );
+}
+
+print "Done !\n";


Property changes on: trunk/PhotoOp/bin/create_user.pl
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/bin/gpsPhoto.pl
===================================================================
--- trunk/PhotoOp/bin/gpsPhoto.pl	                        (rev 0)
+++ trunk/PhotoOp/bin/gpsPhoto.pl	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,435 @@
+#!/usr/bin/perl -w
+
+#Copyright (C) 2005 Peter Sykora, Andreas Neumann
+
+#This program is free software; you can redistribute it and/or
+#modify it under the terms of the GNU General Public License
+#as published by the Free Software Foundation; either version 2
+#of the License, or (at your option) any later version.
+
+#This program is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#GNU General Public License for more details.
+
+#You should have received a copy of the GNU General Public License
+#along with this program; if not, write to the Free Software
+#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+#requirements: perl >5.8, 
+#module Math::Round (Activestate: Math-Round)
+#module Image::ExifTool (Activestate: Image-ExifTool, warning: Activestate has usually an old version of this module that won't work well with NEF files)
+#module Date::Manip (Activestate: DateManip)
+#module XML::TreeBuilder (Activestate: XML-TreeBuilder)
+#module HTML::Element - a prerequisite for XML::TreeBuilder (Activestate: automatically installs this with XML-TreeBuilder)
+#macosx: xcode tools (for gcc compiler)
+#(exiftags: http://www.sno.phy.queensu.ca/~phil/exiftool/)
+
+#version history:
+#version 0.1 initial version (2005-06-05), A. Neumann
+#version 0.2 (2005-10-12) a major rewrite: now we read in gpx (xml) files instead of the plain text file, A. Neumann
+#version 0.3 (2005-10-25) keyhole file output for google earth, P. Sykora
+#version 0.3.1 (2005-10-26) added timeoffset parameter, cleanup and documentation at http://www.carto.net/projects/photoTools/gpsPhoto/, A. Neumann
+#version 0.3.2 (2005-10-26) corrected a bug that stopped the program execution when a "DateTimeOriginal" exif tag was missing, P. Sykora
+#version 0.3.3 (2006-04-27) added timeoffset to the usage string
+#version 0.3.4 (2006-06-15) fixed bug for getting kml filename parameter (did not check for string), fixed a severe bug in kml output when mixing up x and y coordinates, Patrick Valsecchi and Christian Brauchli
+#version 0.3.5 (2006-06-21) added tracks to kml output, Patrick Valsecchi
+#version 0.3.6 (2006-08-18) added support for Canon Raw Files (cr2), Andreas Neumann 
+#version 0.3.7 (2006-08-29) added support for Canon Raw Files / Digital Rebel (crw), P. Sykora 
+
+use strict;
+use File::Find;
+use File::Basename;
+use File::Copy;
+use File::Spec;
+use Getopt::Long;
+use Math::Round;
+use POSIX qw(floor);
+use Date::Manip;
+use Image::ExifTool;
+use XML::TreeBuilder;
+
+my ($version, $program, $date);
+
+my $dir; #directory with images
+my $gpsfile; #text-file containing gps-data
+my ($maxtimediff, $timeoffset); #maximum time-offset in seconds (integer value)
+my $writecaption; #indicates that caption should be copied from file-name
+my $copydate; #indicates that the program should copy the exif date to the iptc tag
+my $credit; #a string value containing photographer and supporting people
+my ($city,$state,$country,$copyright,$source,$keywords); #some iptc geo metadata elements
+my $file; #jpeg file
+my $line; #holding data from a line from the gpsfile
+my %gpsData; #array holding GPS data
+my $time; #temporary timestring
+my $tagList;
+my $secs;
+my $key;
+my $minKey;
+my $mintimediff;
+my ($dateTime,$datum,$month,$day,$year,$fn);
+my ($hour,$minute,$second);
+my ($dummy,$result,$success,$errStr,$trkpt);
+my $kml; #path to keyhole file output
+
+
+$version = "0.3.6";
+$date = "2006-08-18";
+$program = $0;
+$program =~ s/(\.\/)//;
+my $usage = "$program (version $version, $date)\nUsage: $program --dir myphotos/dir/ [--gpsfile yourgpsfile.gpx] [--maxtimediff 60] [--timeoffset 0] [--writecaption] [--copydate] [--credit \"your credit text\"] [--city \"your city name\"] [--state \"your state name\"] [--country \"your country name\"] [--copyright \"your copyright info\"] [--source http://www.carto.net/neumann/] [--keywords \"waterfall,mountains,lakes,hotel,cablecar\"] [--kml \"your kml output for google earth\"]\n";
+
+#get parameters
+GetOptions("dir=s" => \$dir,
+		"gpsfile=s" => \$gpsfile,
+		"credit=s" => \$credit,
+		"city=s" => \$city,
+		"state=s" => \$state,
+		"country=s" => \$country,
+		"copyright=s" => \$copyright,
+		"keywords=s" => \$keywords,
+		"source=s" => \$source,
+		"maxtimediff=i" => \$maxtimediff,
+		"timeoffset=i" => \$timeoffset,
+		"writecaption" => \$writecaption,
+		"copydate" => \$copydate,
+		"kml=s" => \$kml
+	);
+
+unless ($dir) {
+	die "$usage you have to specify a directory containing the images (dir)!\n";
+}
+
+if ($kml) {
+	open(KMLFILE,">$kml") or die "Can't open file $kml: $!\n";
+	my $location = join " - ", ($country||'', $state||'', $city||'');
+	$location =~ s/-  -|- $|^-//;
+	print KMLFILE qq (<?xml version="1.0" encoding="UTF-8"?>
+<kml xmlns="http://earth.google.com/kml/2.0">
+<Document>
+  <name>My images</name>
+  <open>1</open>
+  <description><![CDATA[);
+	if (length($location)>2) { print KMLFILE qq(
+  	<h3>Location</h3>
+	<p>$location</p>);}
+	if ($keywords) { print KMLFILE qq(
+	<h3>Keywords</h3>
+	<p>$keywords</p>);}
+	if ($source) { print KMLFILE qq(
+	<h3>Source</h3>
+	<p>$source</p>);}
+	if ($copydate) { print KMLFILE qq(
+	<h3>Copydate</h3>
+	<p>$copydate</p>);}
+	if ($credit) { print KMLFILE qq(
+	<h3>Credit</h3>
+	<p>$credit</p>);}
+	if ($copyright) { print KMLFILE qq(
+	<h3>Copyright</h3>
+	<p>$copyright</p>);}
+  print KMLFILE qq(
+  ]]></description>
+  <Style id="Photo">
+   <geomScale>.75</geomScale>
+    <IconStyle>
+     <color>ffffffff</color>
+      <Icon>
+       <href>root://icons/palette-4.png</href>
+       <x>192</x>
+       <y>96</y>
+       <w>32</w>
+       <h>32</h>
+      </Icon>
+     </IconStyle>
+  </Style>
+  <Folder>
+   <name>Photos</name>
+   <open>0</open>);
+}
+
+unless ($maxtimediff) {
+	#set timediff to default of one minute
+	$maxtimediff = 120;
+}
+
+unless ($timeoffset) {
+	#set timediff to default of one minute
+	$timeoffset = 0;
+}
+
+my $tree = XML::TreeBuilder->new();
+$tree->parse_file($gpsfile);
+
+my $lineCounter=0;
+foreach $trkpt ($tree->find_by_tag_name('trkpt')) {
+	$dateTime = $trkpt->find_by_tag_name('time')->as_text;
+	($datum,$time,$dummy) = split(/[T|Z]/,$dateTime);
+	($year,$month,$day) = split(/-/,$datum);
+	($hour,$minute,$second) = split(/:/,$time);
+	$secs = Date_SecsSince1970($month,$day,$year,$hour,$minute,$second);
+	$gpsData{$secs}{"x"} = $trkpt->attr_get_i('lon');
+	$gpsData{$secs}{"y"} = $trkpt->attr_get_i('lat');
+	$gpsData{$secs}{"z"} = nearest(1,$trkpt->find_by_tag_name('ele')->as_text);
+	$gpsData{$secs}{"t"} = $time;
+	$gpsData{$secs}{"d"} = $datum;
+	$lineCounter++;
+}
+
+print "\nprocessed $lineCounter coordinates\n\n";
+print "done with reading gps file\n\n";
+
+my $pictureCounter = 0;
+my $pictureCounterCoordinate = 0;
+
+#read directory and process files
+opendir(DIR, $dir) or die "can't open directory $dir: $!";
+print "\nprocessing directory \"$dir\"\n\n";
+while (defined($file = readdir(DIR))) {
+	$file = File::Spec->rel2abs($dir.$file);
+	#first check if it is a jpeg file
+	my ($base, $dir, $ext) = fileparse($file,'\..*');
+	if ($ext eq ".jpg" || $ext eq ".JPG" || $ext eq ".jpeg" || $ext eq ".JPEG" || $ext eq ".nef" || $ext eq ".NEF" || $ext eq ".cr2" || $ext eq ".CR2" || $ext eq ".crw" || $ext eq ".CRW") {
+		my $writeFile = 0;
+		my $dest_file = File::Spec->rel2abs($dir.$base."_1".$ext);
+		my $exifTool = new Image::ExifTool;
+		my $imgInfo = $exifTool->ImageInfo($file,"DateTimeOriginal");
+		my @tags = $exifTool->GetRequestedTags();
+		my $createDate = $exifTool->GetValue($tags[0]);
+		my @dateTime;
+		if ($createDate) {
+			@dateTime = split(/\s+|\+/,$createDate);
+			($year,$month,$day) = split(/:/,$dateTime[0]);
+			($hour,$minute,$second) = split(/:/,$dateTime[1]);
+			$secs = Date_SecsSince1970($month,$day,$year,$hour,$minute,$second) + $timeoffset;
+			#now compare timestamps
+			$mintimediff = $maxtimediff;
+			$minKey = -99;
+			foreach $key (%gpsData) {
+				if (abs($key - $secs) < $mintimediff) {
+					$minKey = $key;
+					$mintimediff = abs($key - $secs);
+				}
+			}
+			print "$file, $createDate, timediff=$mintimediff\n";
+		}
+		else {
+			print "$file - no date available\n";
+			$minKey = -99;
+		}
+		#this part only applies if we find a matching GPS data
+		if (int($minKey) != -99) {
+			my $instructions = "Lat ".$gpsData{$minKey}{"y"}.", Lon ".$gpsData{$minKey}{"x"}." - Bearing: 0 - Altitude: ".$gpsData{$minKey}{"z"}."m";
+			print $instructions."\n";
+			#write coordinates to IPTC field "SpecialInstructions
+			($success, $errStr) = $exifTool->SetNewValue("SpecialInstructions",$instructions,Group=>'IPTC');
+			die "problem writing out IPTC data for file '".$file."', Error: ".$errStr."\n" if $success != 1; 
+			#write out GPS exif tags
+			#write latitude
+			($success, $errStr) = $exifTool->SetNewValue("GPSLatitude",dd2dms(abs($gpsData{$minKey}{"y"})),Group=>'GPS');
+			die "problem writing out GPS latitude data (GPSLatitude) for file '".$file."', Error: ".$errStr."\n" if $success != 1; 
+			if ($gpsData{$minKey}{"y"} > 0) {
+				($success, $errStr) = $exifTool->SetNewValue("GPSLatitudeRef","N",Group=>'GPS');
+				die "problem writing out GPS latitude ref data (GPSLatitudeRef) for file '".$file."', Error: ".$errStr."\n" if $success != 1; 
+			}
+			else {
+				($success, $errStr) = $exifTool->SetNewValue("GPSLatitudeRef","S",Group=>'GPS');
+				die "problem writing out GPS latitude ref data (GPSLatitudeRef) for file '".$file."', Error: ".$errStr."\n" if $success != 1; 			
+			}
+			#write longitude
+			($success, $errStr) = $exifTool->SetNewValue("GPSLongitude",dd2dms(abs($gpsData{$minKey}{"x"})),Group=>'GPS');
+			die "problem writing out GPS longitude data (GPSLongitude) for file '".$file."', Error: ".$errStr."\n" if $success != 1; 
+			if ($gpsData{$minKey}{"x"} > 0) {
+				($success, $errStr) = $exifTool->SetNewValue("GPSLongitudeRef","E",Group=>'GPS');
+				die "problem writing out GPS longitude ref data (GPSLongitudeRef) for file '".$file."', Error: ".$errStr."\n" if $success != 1; 
+			}
+			else {
+				($success, $errStr) = $exifTool->SetNewValue("GPSLongitudeRef","W",Group=>'GPS');
+				die "problem writing out GPS longitude ref data (GPSLongitudeRef) for file '".$file."', Error: ".$errStr."\n" if $success != 1; 			
+			}
+			#write altitude
+			($success, $errStr) = $exifTool->SetNewValue("GPSAltitude",abs($gpsData{$minKey}{"z"}),Group=>'GPS');
+			die "problem writing out GPS altitude data (GPSAltitude) for file '".$file."', Error: ".$errStr."\n" if $success != 1; 
+			if ($gpsData{$minKey}{"z"} > 0) {
+				($success, $errStr) = $exifTool->SetNewValue("GPSAltitudeRef","Above Sea Level",Group=>'GPS');
+				die "problem writing out GPS altitude ref data (GPSAltitudeRef) for file '".$file."', Error: ".$errStr."\n" if $success != 1; 
+			}
+			else {
+				($success, $errStr) = $exifTool->SetNewValue("GPSAltitudeRef","Below Sea Level",Group=>'GPS');
+				die "problem writing out GPS altitude ref data (GPSAltitudeRef) for file '".$file."', Error: ".$errStr."\n" if $success != 1; 			
+			}
+			#write timestamp
+			($success, $errStr) = $exifTool->SetNewValue("GPSTimeStamp",$gpsData{$minKey}{"t"},Group=>'GPS');
+			die "problem writing out GPS timestamp data (GPSTimeStamp) for file '".$file."', Error: ".$errStr."\n" if $success != 1; 
+			#write datestamp
+			($success, $errStr) = $exifTool->SetNewValue("GPSDateStamp",$gpsData{$minKey}{"d"},Group=>'GPS');
+			die "problem writing out GPS timestamp data (GPSDateStamp) for file '".$file."', Error: ".$errStr."\n" if $success != 1; 
+			#write map datum to WGS84
+			($success, $errStr) = $exifTool->SetNewValue("GPSMapDatum","WGS-84",Group=>'GPS');
+			die "problem writing out GPS map datum data (GPSMapDatum) for file '".$file."', Error: ".$errStr."\n" if $success != 1; 
+			#write destination bearing
+			($success, $errStr) = $exifTool->SetNewValue("GPSImgDirection",0,Group=>'GPS');
+			die "problem writing out GPS image bearing data (GPSImgDirection) for file '".$file."', Error: ".$errStr."\n" if $success != 1; 
+			($success, $errStr) = $exifTool->SetNewValue("GPSImgDirectionRef","T",Group=>'GPS');
+			die "problem writing out GPS image bearing reference data (GPSImgDirectionRef) for file '".$file."', Error: ".$errStr."\n" if $success != 1; 
+			$writeFile = 1;
+			$pictureCounterCoordinate++;
+			
+			if ($kml) {
+				($dummy,$dummy,$fn) = File::Spec->splitpath( $file );
+				print KMLFILE qq(
+    <Placemark>
+      <name>$fn</name>
+      <description><![CDATA[<img src="$file" width="200" /><br><a href="$file">full size</a>]]></description>
+      <Snippet/>
+      <LookAt>
+        <longitude>$gpsData{$minKey}{"x"}</longitude>
+        <latitude>$gpsData{$minKey}{"y"}</latitude>
+        <range>10000</range>
+        <tilt>50</tilt>
+        <heading>0</heading>
+      </LookAt>
+      <styleUrl>#Photo</styleUrl>
+      <Point>
+        <altitudeMode>clampToGround</altitudeMode>
+        <coordinates>$gpsData{$minKey}{"x"},$gpsData{$minKey}{"y"},0</coordinates>
+      </Point>
+    </Placemark>);
+    				}
+		}
+		else {
+			print "Could not find a coordinate\n";		
+		}
+		if ($writecaption) {
+			$writeFile = 1;
+			my $caption = $base;
+			$caption =~ s/^\d+\_//;
+			$caption =~ s/(\_.)/\U$1/g;
+			$caption =~ s/\_/ /g;
+			$caption = ucfirst($caption);
+			($success, $errStr) = $exifTool->SetNewValue("Caption-Abstract",$caption,Group=>'IPTC');
+			die "problem writing out caption data (Caption-Abstract) for file '".$file."', Error: ".$errStr."\n" if $success != 1; 			
+			($success, $errStr) = $exifTool->SetNewValue("ObjectName",$caption,Group=>'IPTC');
+			die "problem writing out title data (ObjectName) for file '".$file."', Error: ".$errStr."\n" if $success != 1; 			
+		}
+		#copy exif date to IPTC CreateDate
+		if ($copydate) {
+			$writeFile = 1;
+			($success, $errStr) = $exifTool->SetNewValue("DateCreated",$dateTime[0],Group=>'IPTC');
+			die "problem writing out IPTC creation date data (DateCreated) for file '".$file."', Error: ".$errStr."\n" if $success != 1; 
+		}
+		#fill in credits (photographer and supporting people)
+		if ($credit) {
+			$writeFile = 1;
+			($success, $errStr) = $exifTool->SetNewValue("Credit",$credit,Group=>'IPTC');
+			die "problem writing out IPTC credit data (DateCreated) for file '".$file."', Error: ".$errStr."\n" if $success != 1; 
+		}
+		#fill in city data
+		if ($city) {
+			$writeFile = 1;
+			($success, $errStr) = $exifTool->SetNewValue("City",$city,Group=>'IPTC');
+			die "problem writing out IPTC city data (City) for file '".$file."', Error: ".$errStr."\n" if $success != 1; 
+		}
+		#fill in state/province data
+		if ($state) {
+			$writeFile = 1;
+			($success, $errStr) = $exifTool->SetNewValue("Province-State",$state,Group=>'IPTC');
+			die "problem writing out IPTC state/province data (Province-State) for file '".$file."', Error: ".$errStr."\n" if $success != 1; 
+		}
+		#fill in country data
+		if ($country) {
+			$writeFile = 1;
+			($success, $errStr) = $exifTool->SetNewValue("Country-PrimaryLocationName",$country,Group=>'IPTC');
+			die "problem writing out IPTC country data (Country-PrimaryLocationName) for file '".$file."', Error: ".$errStr."\n" if $success != 1; 
+		}
+		#fill in copyright data
+		if ($copyright) {
+			$writeFile = 1;
+			($success, $errStr) = $exifTool->SetNewValue("CopyrightNotice",$copyright,Group=>'IPTC');
+			die "problem writing out IPTC copyright notice data (CopyrightNotice) for file '".$file."', Error: ".$errStr."\n" if $success != 1; 
+		}
+		#fill in source (URL) data
+		if ($source) {
+			$writeFile = 1;
+			($success, $errStr) = $exifTool->SetNewValue("Source",$source,Group=>'IPTC');
+			die "problem writing out IPTC source data (Source) for file '".$file."', Error: ".$errStr."\n" if $success != 1; 
+		}
+		#fill in keyword data
+		if ($keywords) {
+			$writeFile = 1;
+			($success, $errStr) = $exifTool->SetNewValue("Keywords",$keywords,Group=>'IPTC');
+			die "problem writing out IPTC keywords data (Keywords) for file '".$file."', Error: ".$errStr."\n" if $success != 1; 
+		}
+		#Finally write out the new metadata
+		if ($writeFile == 1) {
+			$success = $exifTool->WriteInfo($file, $dest_file);
+			if ($success != 1) {
+				$errStr = $exifTool->GetValue('Error');
+				die "\nerror writing $dest_file, error:  $errStr !!!!!!!!!!!!!!!!!!!\n\n";
+			}
+			else {
+				$result = move($dest_file,$file);
+				if (!$result) {
+					die "\nerror replacing $file with $dest_file !!!!!!!!!!!!!!!!!!!!\n\n";	
+				}
+			}
+		}
+		$pictureCounter++;
+	}
+}
+closedir(DIR);
+print "found coordinates for $pictureCounterCoordinate images out of $pictureCounter images ... done\n";
+
+if ($kml) {
+	printKmlTrack();
+	print KMLFILE qq(
+ </Folder>
+</Document>
+</kml>);
+	close(KMLFILE) or die "Can't close file $kml: $!\n";
+}
+
+sub dd2dms {
+	my $dd = shift;
+	my $minutes = ($dd - floor($dd)) * 60;
+	my $seconds = ($minutes - floor($minutes)) * 60;
+	$minutes = floor($minutes);
+	my $degrees = floor($dd);
+	return $degrees.",".$minutes.",".$seconds;
+}
+
+sub printKmlTrack {
+	print KMLFILE qq(
+  <Folder>
+    <name>Track</name>
+    <Placemark>
+      <name>Track</name>
+      <visibility>1</visibility>
+      <Style>
+        <LineStyle>
+          <color>7fffffff</color>
+          <width>4</width>
+        </LineStyle>
+        <PolyStyle>
+          <color>7fffffff</color>
+        </PolyStyle>
+      </Style>
+      <LineString>
+        <extrude>1</extrude>
+        <tessellate>1</tessellate>
+        <altitudeMode>clampToGround</altitudeMode>
+        <coordinates>);
+
+        my $cpt=1;
+	my $secs;
+	foreach $secs (sort keys %gpsData) {
+	        print KMLFILE "</coordinates><coordinates>\n" if($cpt%200==0);
+		print KMLFILE "$gpsData{$secs}{x}, $gpsData{$secs}{y},2\n";
+	}
+
+	print KMLFILE qq(
+	</coordinates>
+      </LineString>
+    </Placemark>
+  </Folder>);
+}


Property changes on: trunk/PhotoOp/bin/gpsPhoto.pl
___________________________________________________________________
Name: svn:executable
   + *

Modified: trunk/PhotoOp/bin/sort_pics.pl
===================================================================
--- trunk/PhotoOp/bin/sort_pics.pl	2009-02-06 21:57:18 UTC (rev 5415)
+++ trunk/PhotoOp/bin/sort_pics.pl	2009-02-07 13:12:54 UTC (rev 5416)
@@ -36,8 +36,45 @@
     my $newdir = $directory->subdir($year, $month);
     $newdir->mkpath();
 
-    print "From ", $file, "To ", $newdir->file($filename), "\n";
-    next if(-e $newdir->file($filename));
+    my $newfile = $newdir->file($filename);
+    $newfile = de_dupe($newfile, qr/dsc(\d+)/);
+    print "From ", $file, "To ", $newfile, "\n";
+    
+    next if(-e $newfile);
 
-    system("cp", "-a", $file, $newdir->file($filename));
+    system("cp", "-a", $file, $newfile);
 }
+
+sub de_dupe
+{
+    my ($file, $match) = @_;
+
+    my $dir = $file->dir;
+    my $maxnum = 0;
+    my $found = 0;
+    while (my $thisfile = $dir->next)
+    {
+        next if($thisfile->is_dir);
+        if(lc $file->basename eq lc $thisfile->basename)
+        {
+            $found = 1;
+            ## No workee?
+            return $file if(-s $file == -s $thisfile);
+        }
+        if($thisfile->basename =~ /dsc(\d+)/i)
+        {
+#            print "Match: $1\n";
+            $maxnum = $1;
+        }
+    }
+    
+    if($found)
+    {
+        my $newnum = sprintf("%05d", $maxnum+1);
+        my $newfile = $file->basename;
+        $newfile =~ s/\d+/$newnum/;
+        return $dir->file($newfile);
+    }
+
+    return $file;
+}

Modified: trunk/PhotoOp/lib/PhotoOp/Controller/Root.pm
===================================================================
--- trunk/PhotoOp/lib/PhotoOp/Controller/Root.pm	2009-02-06 21:57:18 UTC (rev 5415)
+++ trunk/PhotoOp/lib/PhotoOp/Controller/Root.pm	2009-02-07 13:12:54 UTC (rev 5416)
@@ -46,11 +46,14 @@
 sub auto : Private {
     my ($self, $c) = @_;
 
+    $c->log->_dump($c->req->headers);
+#    $c->log->_dump(\%ENV);
+#    $c->log->info("User: $ENV{REMOTE_USER}");
 #    eval {
         $c->authorization_required( realm => 'PhotoOp');
 #    };
 
-    $c->log->debug("ERRORS");
+    $c->log->debug("ERRORS") if ($@);
     $c->log->_dump($@) if($@);
     $c->log->_dump($c->error) if($c->error);
 #    $c->authorization_basic( realm => 'PhotoOp');
@@ -140,6 +143,10 @@
         }
       }
     }
+
+    my $sets = $c->model('MyFlickr')->execute_method('flickr.photosets.getList');
+    $c->log->_dump($sets);
+
     $c->stash->{edittags} = { map {$_ => 1} @{ $c->config->{edittags} } };
     $c->stash->{showtags} = $c->config->{showtags};
     $c->stash->{image} = $image;
@@ -181,7 +188,8 @@
 
     my $tool = Image::ExifTool->new();
     my $iinfo = $tool->ImageInfo("$file");
-    `exiftran -ibp $direction $file`;
+    $c->log->debug("exiftran -ibp $direction $file");
+    $c->log->debug(`exiftran -ibp $direction $file`);
 
     $tool->ExtractInfo("$file");
     foreach my $key (keys %$iinfo)
@@ -363,7 +371,7 @@
     print STDERR "is_private: ", caller(), "\n";
     my $image = $c->model('Image')->new({file => $file});
     $c->log->debug("Body length is: " . length( $c->response->body ));
-    if(!$c->check_user_roles('admin') &&
+    if(!$c->check_user_roles('admin') && $image->{tags}{UserComment} &&
        $image->{tags}{UserComment} =~ /\bprivate\b/i)
     {
         $c->res->content_type('text/plain');
@@ -387,6 +395,7 @@
                       'http://localhost:3002/' => 'ABQIAAAAS5Z5Qenoms4bZM9gkhc_wRRygTfTQWrasUG65-9lrppCOXwEVBTXZiN5FrqdFaUCDHH5_yAAd6OpBw',
                       'http://desert-island.dynodns.net:3000/' => 'ABQIAAAAS5Z5Qenoms4bZM9gkhc_wRQqasjHjp5pBjHpCjE9bZEaHD0KjhQGGKlNF5oHIHVmHoSPxlkCq9-n6g',
                       'http://desert-island:3000/' => 'ABQIAAAAS5Z5Qenoms4bZM9gkhc_wRSaj8_4VciYG4qh_kMMn-Sz0AqvrxQmjslHzomTfHZ4MATbSZAH5KZUCg',
+                      'http://d-i:3000/' => 'ABQIAAAAS5Z5Qenoms4bZM9gkhc_wRTg37uec2ihLedn0QpMEGyZCqwKUBSlGlH5pOQzqLA3JrSy_agt4iq6Hw',
                      );
 
     return $googlekeys{$c->req->base};
@@ -409,6 +418,8 @@
 
 sub end : ActionClass('RenderView')
 {
+#    my ($self, $c) = shift;
+#    $c->log->_dump($c->res);
 }
 
 =head1 AUTHOR

Modified: trunk/PhotoOp/lib/PhotoOp/Model/Image.pm
===================================================================
--- trunk/PhotoOp/lib/PhotoOp/Model/Image.pm	2009-02-06 21:57:18 UTC (rev 5415)
+++ trunk/PhotoOp/lib/PhotoOp/Model/Image.pm	2009-02-07 13:12:54 UTC (rev 5416)
@@ -5,19 +5,20 @@
 ## no importing file() and path()
 use Path::Class ();
 
-my $tool = Image::ExifTool->new;
+# my $tool = Image::ExifTool->new;
 
 sub new
 {
   $|++; # DEBUG
     my ($class, $args) = @_;
     my $self = bless($args || {}, $class);
+    $self->{_tool} = Image::ExifTool->new();
 
     $self->{file} = Path::Class::file($self->{file})
       unless ref $self->{file} and
         $self->{file}->isa('Path::Class');
 
-    $self->{tags} = $tool->ImageInfo(''.$self->{file}, '*', 
+    $self->{tags} = $self->{_tool}->ImageInfo(''.$self->{file}, '*', 
                                      {CoordFormat => '%.8f',
                                       DateFormat => '%Y:%m:%d %H:%M:%S',
                                       Unknown => 2});
@@ -43,6 +44,17 @@
   return $self->{file};
 }
 
+sub tag
+{
+    my ($self, $tagname) = @_;
+
+    print STDERR "In tag, request for: $tagname\n";
+    my $stuff = $self->{_tool}->GetInfo($tagname);
+    print STDERR "In tag, values: ", Data::Dumper::Dumper($stuff);
+
+    return $stuff->{ (keys(%$stuff))[0] };
+}
+
 sub get_tags
 {
   my ($self, @tags) = @_;
@@ -57,7 +69,7 @@
 
   return $self->{thumbnail} if exists $self->{thumbnail};
   
-  $self->{thumbnail} = $tool->ImageInfo($self->{file}->stringify, 'ThumbnailImage');
+  $self->{thumbnail} = $self->{_tool}->ImageInfo($self->{file}->stringify, 'ThumbnailImage');
   
   return $self->{thumbnail};
 }
@@ -94,3 +106,6 @@
 
   return $self;
 }
+
+1;
+

Added: trunk/PhotoOp/lib/PhotoOp/Model/MyFlickr.pm
===================================================================
--- trunk/PhotoOp/lib/PhotoOp/Model/MyFlickr.pm	                        (rev 0)
+++ trunk/PhotoOp/lib/PhotoOp/Model/MyFlickr.pm	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,7 @@
+package PhotoOp::Model::MyFlickr;
+use base 'Catalyst::Model::Flickr';
+
+use strict;
+use warnings;
+
+1;


Property changes on: trunk/PhotoOp/lib/PhotoOp/Model/MyFlickr.pm
___________________________________________________________________
Name: svn:executable
   + *

Modified: trunk/PhotoOp/lib/PhotoOp.pm
===================================================================
--- trunk/PhotoOp/lib/PhotoOp.pm	2009-02-06 21:57:18 UTC (rev 5415)
+++ trunk/PhotoOp/lib/PhotoOp.pm	2009-02-07 13:12:54 UTC (rev 5416)
@@ -40,10 +40,14 @@
                                   'DateTimeOriginal',
                                   'ImageWidth',
                                   'ImageHeight',
+                                  'IPTC:ObjectName',
                                   'DocumentName',
                                   'ImageDescription',
                                   'Artist',
                                   'Copyright',
+                                  'IPTC:Keywords',
+                                  'IPTC:City',
+                                  'IPTC:Country',
                                   'UserComment',
                                   'GPSLatitudeRef',     # S/N
                                   'GPSLatitude',
@@ -51,10 +55,14 @@
                                   'GPSLongitude',
                                   ],
                      edittags => [
+                                  'IPTC:ObjectName',
                                   'DocumentName',
                                   'ImageDescription',
                                   'Artist',
                                   'Copyright',
+                                  'IPTC:Keywords',
+                                  'IPTC:City',
+                                  'IPTC:Country',
                                   'UserComment',
                                   'GPSLatitudeRef',     # S/N
                                   'GPSLatitude',

Modified: trunk/PhotoOp/root/editfile.tt
===================================================================
--- trunk/PhotoOp/root/editfile.tt	2009-02-06 21:57:18 UTC (rev 5415)
+++ trunk/PhotoOp/root/editfile.tt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -31,7 +31,7 @@
      [% FOREACH tag = showtags %]
       <div class="tagvalue" id="[% tag %]">
        [%- IF edittags.$tag -%]<span dojoType="inlineEditBox" onSave="commitchange">[% END -%]
-        [%- image.tags.$tag ? image.tags.$tag : "<i>None</i>" %]
+        [%- image.tag(tag) ? image.tag(tag) : "<i>None</i>" %]
        [%- IF edittags.$tag -%]</span>[% END -%]
       </div>
      [% END; %]

Modified: trunk/PhotoOp/root/header.tt
===================================================================
--- trunk/PhotoOp/root/header.tt	2009-02-06 21:57:18 UTC (rev 5415)
+++ trunk/PhotoOp/root/header.tt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -28,7 +28,7 @@
   [%- IF googlekey -%]
   <script src="http://maps.google.com/maps?file=api&amp;v=2&amp;key=[%- googlekey -%]" type="text/javascript"></script>
   [%- END -%]
-  <script src="http://openlayers.org/api/2/OpenLayers.js"></script> 
+  <script src="http://www.openlayers.org/api/2/OpenLayers.js"></script> 
 <!--  <script src="/static/js/OpenLayers-2.2/lib/OpenLayers.js"></script> -->
   <title>[%- name -%]</title>
   <link title="[%- name -%]" href="/static/css/[%- name | lower -%].css" type="text/css" rel="stylesheet" />

Added: trunk/PhotoOp/root/login.tt
===================================================================
--- trunk/PhotoOp/root/login.tt	                        (rev 0)
+++ trunk/PhotoOp/root/login.tt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,5 @@
+[% PROCESS "header.tt" %]
+<body>
+  [% IF message; message; END %]
+</body>
+</html>


Property changes on: trunk/PhotoOp/root/login.tt
___________________________________________________________________
Name: svn:executable
   + *

Modified: trunk/PhotoOp/root/openlayers.tt
===================================================================
--- trunk/PhotoOp/root/openlayers.tt	2009-02-06 21:57:18 UTC (rev 5415)
+++ trunk/PhotoOp/root/openlayers.tt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -18,7 +18,7 @@
    var marker = new OpenLayers.Marker(
         new OpenLayers.LonLat([% lon %], [% lat %]), 
           new OpenLayers.Icon(
-           "http://openlayers.org/api/img/marker.png", 
+           "http://www.openlayers.org/api/img/marker.png", 
            new OpenLayers.Size(10,15)
           ));
     markersLayer.addMarker(marker);

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/arrows.svg
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/arrows.svg	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/arrows.svg	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://inkscape.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="18.000000px"
+   height="18.000000px"
+   id="svg2"
+   sodipodi:version="0.32"
+   inkscape:version="0.42"
+   sodipodi:docbase="/Users/phil/Documents/work/metacarta/2svn.openlayers.net/follower/code/b-edits-1/openlayers/assets/ui_elements"
+   sodipodi:docname="pan_icons.svg"
+   inkscape:export-filename="/Users/phil/Documents/work/metacarta/2svn.openlayers.net/follower/code/b-edits-1/openlayers/assets/ui_elements/zoom-minus-mini.png"
+   inkscape:export-xdpi="90.000000"
+   inkscape:export-ydpi="90.000000">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1.0000000"
+     inkscape:cx="9.0000000"
+     inkscape:cy="9.0000000"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     inkscape:window-width="1226"
+     inkscape:window-height="800"
+     inkscape:window-x="42"
+     inkscape:window-y="22" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="background"
+     inkscape:groupmode="layer"
+     id="layer1">
+    <g
+       id="g2110">
+      <path
+         transform="matrix(0.000000,-1.003160,1.003160,0.000000,-2.844000e-2,18.02844)"
+         d="M 17.971654 9.0000000 A 8.9716539 8.9716539 0 1 1  0.028346062,9.0000000 A 8.9716539 8.9716539 0 1 1  17.971654 9.0000000 z"
+         sodipodi:ry="8.9716539"
+         sodipodi:rx="8.9716539"
+         sodipodi:cy="9.0000000"
+         sodipodi:cx="9.0000000"
+         id="path2059"
+         style="fill:#00008b;fill-opacity:1.0000000;stroke:none;stroke-width:39.984146;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000"
+         sodipodi:type="arc" />
+      <rect
+         transform="matrix(0.000000,-1.000000,1.000000,0.000000,0.000000,0.000000)"
+         y="4.3599998e-06"
+         x="-9.0000048"
+         height="18.000000"
+         width="9.0000000"
+         id="rect2061"
+         style="fill:#00008b;fill-opacity:1.0000000;stroke:none;stroke-width:39.984146;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000" />
+    </g>
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer4"
+     inkscape:label="zoom"
+     style="display:inline">
+    <text
+       xml:space="preserve"
+       style="font-size:18.000000px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125.00000%;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1.0000000;stroke:#ffffff;stroke-width:1.0000000px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1.0000000;font-family:Helvetica"
+       x="3.7485352"
+       y="-3.4077148"
+       id="text2114"
+       sodipodi:linespacing="125.00000%"
+       transform="scale(1.000000,-1.000000)"><tspan
+         sodipodi:role="line"
+         id="tspan2118"
+         x="3.7485352"
+         y="-3.4077148">−</tspan></text>
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer2"
+     inkscape:label="overlay"
+     style="display:none">
+    <path
+       style="font-size:12.000000px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125.00000%;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1.0000000;stroke:none;stroke-width:1.0000000px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;font-family:Verdana"
+       d="M 2.9678210,9.0332227 C 2.9678314,9.5015070 3.1752531,9.8938504 3.5900866,10.210254 C 3.6463464,10.254553 3.9451742,10.453889 4.4865710,10.808262 L 8.1568835,13.247754 C 8.2693886,13.323691 8.4240759,13.393300 8.6209460,13.456582 C 8.8107943,13.519862 9.0041535,13.551503 9.2010240,13.551504 C 9.5736843,13.551503 9.8936056,13.431269 10.160789,13.190801 C 10.427980,12.950332 10.561574,12.659238 10.561572,12.317520 C 10.561574,11.861896 10.322511,11.488536 9.8443836,11.197442 L 8.5998522,10.428575 L 14.548408,10.428575 C 15.026535,10.428577 15.406221,10.298849 15.687470,10.039395 C 15.968721,9.7799443 16.109345,9.4318977 16.109345,8.9952540 C 16.109345,8.5396330 15.961690,8.1915865 15.666377,7.9511133 C 15.364034,7.7106494 14.980831,7.5904152 14.516767,7.5904102 L 8.6314928,7.5904102 L 9.9920390,6.7076368 C 10.160793,6.5937365 10.290871,6.4481897 10.382274,6.2709962 C 10.466652,6.0874870 10.508840,5.8913153 10.508837,5.6824805 C 10.508840,5.3281127 10.378761,5.0338552 10.118602,4.7997070 C 9.8584498,4.5655745 9.5279814,4.4485043 9.1271961,4.4484962 C 8.7615757,4.4485043 8.4029823,4.5624104 8.0514147,4.7902149 L 3.9697741,7.5239649 C 3.6393151,7.7454540 3.4002529,7.9542820 3.2525866,8.1504493 C 3.0627532,8.3972503 2.9678314,8.6756875 2.9678210,8.9857618 L 2.9678210,9.0332227"
+       id="text2082"
+       sodipodi:nodetypes="csccsssscccssscccssssccscc" />
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer3"
+     inkscape:label="pan triangle"
+     style="display:none">
+    <path
+       sodipodi:type="star"
+       style="fill:#ffffff;fill-opacity:1.0000000;stroke:none;stroke-width:39.984146;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000"
+       id="path2107"
+       sodipodi:sides="3"
+       sodipodi:cx="6.8031497"
+       sodipodi:cy="5.1874018"
+       sodipodi:r1="5.0880113"
+       sodipodi:r2="2.5440056"
+       sodipodi:arg1="1.0471976"
+       sodipodi:arg2="2.0943951"
+       inkscape:flatsided="false"
+       inkscape:rounded="0.0000000"
+       inkscape:randomized="0.0000000"
+       d="M 9.3471551,9.5937489 L 5.5311469,7.3905753 L 1.7151384,5.1874015 L 5.5311469,2.9842283 L 9.3471555,0.78105489 L 9.3471553,5.1874018 L 9.3471551,9.5937489 z "
+       transform="matrix(0.000000,-1.331000,1.331000,0.000000,2.038879,16.92102)" />
+  </g>
+</svg>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/arrows.svg
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/layer-switcher-maximize.svg
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/layer-switcher-maximize.svg	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/layer-switcher-maximize.svg	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,128 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://inkscape.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="18.000000px"
+   height="18.000000px"
+   id="svg2"
+   sodipodi:version="0.32"
+   inkscape:version="0.43"
+   sodipodi:docbase="/home/sderle/projects/openlayers/layerswitcher/art"
+   sodipodi:docname="layer-switcher-maximize.svg"
+   inkscape:export-filename="/Users/phil/Documents/work/metacarta/2svn.openlayers.net/follower/code/b-edits-1/openlayers/assets/ui_elements/zoom-minus-mini.png"
+   inkscape:export-xdpi="90.000000"
+   inkscape:export-ydpi="90.000000">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="16"
+     inkscape:cx="9"
+     inkscape:cy="9"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer4"
+     inkscape:window-width="1226"
+     inkscape:window-height="800"
+     inkscape:window-x="42"
+     inkscape:window-y="25" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="background"
+     inkscape:groupmode="layer"
+     id="layer1">
+    <g
+       id="g2110"
+       transform="matrix(-7.849624e-17,1,-1,-7.849624e-17,18,0)">
+      <path
+         transform="matrix(0,-1.00316,1.00316,0,-2.844e-2,18.02844)"
+         d="M 17.971654 9 A 8.9716539 8.9716539 0 1 1  0.028346062,9 A 8.9716539 8.9716539 0 1 1  17.971654 9 z"
+         sodipodi:ry="8.9716539"
+         sodipodi:rx="8.9716539"
+         sodipodi:cy="9"
+         sodipodi:cx="9"
+         id="path2059"
+         style="fill:#00008b;fill-opacity:1;stroke:none;stroke-width:39.98414612;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         sodipodi:type="arc" />
+      <rect
+         transform="matrix(0,-1,1,0,0,0)"
+         y="4.3599998e-06"
+         x="-9.0000048"
+         height="18"
+         width="9"
+         id="rect2061"
+         style="fill:#00008b;fill-opacity:1;stroke:none;stroke-width:39.98414612;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+    </g>
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer4"
+     inkscape:label="zoom"
+     style="display:inline">
+    <text
+       xml:space="preserve"
+       style="font-size:18px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1;display:inline;font-family:Helvetica"
+       x="1.5214844"
+       y="-3.2949219"
+       id="text2114"
+       sodipodi:linespacing="125%"
+       transform="scale(1,-1)"><tspan
+         sodipodi:role="line"
+         id="tspan2118"
+         x="1.5214844"
+         y="-3.2949219">+</tspan></text>
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer2"
+     inkscape:label="overlay"
+     style="display:none">
+    <path
+       style="font-size:12.000000px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125.00000%;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1.0000000;stroke:none;stroke-width:1.0000000px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;font-family:Verdana"
+       d="M 2.9678210,9.0332227 C 2.9678314,9.5015070 3.1752531,9.8938504 3.5900866,10.210254 C 3.6463464,10.254553 3.9451742,10.453889 4.4865710,10.808262 L 8.1568835,13.247754 C 8.2693886,13.323691 8.4240759,13.393300 8.6209460,13.456582 C 8.8107943,13.519862 9.0041535,13.551503 9.2010240,13.551504 C 9.5736843,13.551503 9.8936056,13.431269 10.160789,13.190801 C 10.427980,12.950332 10.561574,12.659238 10.561572,12.317520 C 10.561574,11.861896 10.322511,11.488536 9.8443836,11.197442 L 8.5998522,10.428575 L 14.548408,10.428575 C 15.026535,10.428577 15.406221,10.298849 15.687470,10.039395 C 15.968721,9.7799443 16.109345,9.4318977 16.109345,8.9952540 C 16.109345,8.5396330 15.961690,8.1915865 15.666377,7.9511133 C 15.364034,7.7106494 14.980831,7.5904152 14.516767,7.5904102 L 8.6314928,7.5904102 L 9.9920390,6.7076368 C 10.160793,6.5937365 10.290871,6.4481897 10.382274,6.2709962 C 10.466652,6.0874870 10.508840,5.8913153 10.508837,5.6824805 C 10.508840,5.3281127 10.378761,5.0338552 10.118602,4.7997070 C 9.8584498,4.5655745 9.5279814,4.4485043 9.1271961,4.4484962 C 8.7615757,4.4485043 8.4029823,4.5624104 8.0514147,4.7902149 L 3.9697741,7.5239649 C 3.6393151,7.7454540 3.4002529,7.9542820 3.2525866,8.1504493 C 3.0627532,8.3972503 2.9678314,8.6756875 2.9678210,8.9857618 L 2.9678210,9.0332227"
+       id="text2082"
+       sodipodi:nodetypes="csccsssscccssscccssssccscc" />
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer3"
+     inkscape:label="pan triangle"
+     style="display:none">
+    <path
+       sodipodi:type="star"
+       style="fill:#ffffff;fill-opacity:1.0000000;stroke:none;stroke-width:39.984146;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000"
+       id="path2107"
+       sodipodi:sides="3"
+       sodipodi:cx="6.8031497"
+       sodipodi:cy="5.1874018"
+       sodipodi:r1="5.0880113"
+       sodipodi:r2="2.5440056"
+       sodipodi:arg1="1.0471976"
+       sodipodi:arg2="2.0943951"
+       inkscape:flatsided="false"
+       inkscape:rounded="0.0000000"
+       inkscape:randomized="0.0000000"
+       d="M 9.3471551,9.5937489 L 5.5311469,7.3905753 L 1.7151384,5.1874015 L 5.5311469,2.9842283 L 9.3471555,0.78105489 L 9.3471553,5.1874018 L 9.3471551,9.5937489 z "
+       transform="matrix(0.000000,-1.331000,1.331000,0.000000,2.038879,16.92102)" />
+  </g>
+</svg>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/layer-switcher-maximize.svg
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/layer-switcher-minimize.svg
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/layer-switcher-minimize.svg	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/layer-switcher-minimize.svg	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,142 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://inkscape.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   inkscape:export-ydpi="90.000000"
+   inkscape:export-xdpi="90.000000"
+   inkscape:export-filename="/home/sderle/projects/openlayers/layerswitcher/img/layer-switcher-minimize.png"
+   sodipodi:docname="layer-switcher-minimize.svg"
+   sodipodi:docbase="/home/sderle/projects/openlayers/layerswitcher/art"
+   inkscape:version="0.43"
+   sodipodi:version="0.32"
+   id="svg2"
+   height="18.000000px"
+   width="18.000000px">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     inkscape:window-y="26"
+     inkscape:window-x="42"
+     inkscape:window-height="800"
+     inkscape:window-width="1226"
+     inkscape:current-layer="layer1"
+     inkscape:document-units="px"
+     inkscape:cy="9.3025513"
+     inkscape:cx="9"
+     inkscape:zoom="16"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     borderopacity="1.0"
+     bordercolor="#666666"
+     pagecolor="#ffffff"
+     id="base" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     style="display:inline"
+     id="layer1"
+     inkscape:groupmode="layer"
+     inkscape:label="background">
+    <rect
+       style="fill:#00008b;fill-opacity:0;stroke:none;stroke-width:39.984146;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000"
+       id="rect2061"
+       width="18.014498"
+       height="18.000000"
+       x="-18.014502"
+       y="4.3599998e-06"
+       transform="matrix(0.000000,-1.000000,1.000000,0.000000,0.000000,0.000000)"
+       inkscape:export-xdpi="90"
+       inkscape:export-ydpi="90"
+       inkscape:export-filename="/home/sderle/projects/openlayers/layerswitcher/art/layer-switcher-minimize.png" />
+  </g>
+  <g
+     inkscape:label="graticule"
+     id="layer5"
+     inkscape:groupmode="layer">
+    <text
+       xml:space="preserve"
+       style="font-size:18px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1;display:inline;font-family:Helvetica"
+       x="5.2802601"
+       y="-3.8032362"
+       id="text1432"
+       sodipodi:linespacing="125%"
+       transform="scale(1,-1)"><tspan
+         sodipodi:role="line"
+         id="tspan1434"
+         x="5.2802601"
+         y="-3.8032362">-</tspan></text>
+    <rect
+       style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.99699599px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
+       id="rect1438"
+       width="13.003004"
+       height="13.002999"
+       x="2.498498"
+       y="2.4984975" />
+  </g>
+  <g
+     style="display:none"
+     inkscape:label="zoom"
+     id="layer4"
+     inkscape:groupmode="layer">
+    <text
+       transform="scale(1.000000,-1.000000)"
+       sodipodi:linespacing="125.00000%"
+       id="text2114"
+       y="-3.4077148"
+       x="3.7485352"
+       style="font-size:18.000000;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;fill:#ffffff;fill-opacity:1.0000000;stroke:#ffffff;stroke-width:1.0000000px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1.0000000;font-family:Helvetica;text-anchor:start;writing-mode:lr-tb"
+       xml:space="preserve"><tspan
+         y="-3.4077148"
+         x="3.7485352"
+         id="tspan2118"
+         sodipodi:role="line">−</tspan></text>
+  </g>
+  <g
+     style="display:none"
+     inkscape:label="overlay"
+     id="layer2"
+     inkscape:groupmode="layer">
+    <path
+       sodipodi:nodetypes="csccsssscccssscccssssccscc"
+       id="text2082"
+       d="M 2.9678210,9.0332227 C 2.9678314,9.5015070 3.1752531,9.8938504 3.5900866,10.210254 C 3.6463464,10.254553 3.9451742,10.453889 4.4865710,10.808262 L 8.1568835,13.247754 C 8.2693886,13.323691 8.4240759,13.393300 8.6209460,13.456582 C 8.8107943,13.519862 9.0041535,13.551503 9.2010240,13.551504 C 9.5736843,13.551503 9.8936056,13.431269 10.160789,13.190801 C 10.427980,12.950332 10.561574,12.659238 10.561572,12.317520 C 10.561574,11.861896 10.322511,11.488536 9.8443836,11.197442 L 8.5998522,10.428575 L 14.548408,10.428575 C 15.026535,10.428577 15.406221,10.298849 15.687470,10.039395 C 15.968721,9.7799443 16.109345,9.4318977 16.109345,8.9952540 C 16.109345,8.5396330 15.961690,8.1915865 15.666377,7.9511133 C 15.364034,7.7106494 14.980831,7.5904152 14.516767,7.5904102 L 8.6314928,7.5904102 L 9.9920390,6.7076368 C 10.160793,6.5937365 10.290871,6.4481897 10.382274,6.2709962 C 10.466652,6.0874870 10.508840,5.8913153 10.508837,5.6824805 C 10.508840,5.3281127 10.378761,5.0338552 10.118602,4.7997070 C 9.8584498,4.5655745 9.5279814,4.4485043 9.1271961,4.4484962 C 8.7615757,4.4485043 8.4029823,4.5624104 8.0514147,4.7902149 L 3.9697741,7.5239649 C 3.6393151,7.7454540 3.4002529,7.9542820 3.2525866,8.1504493 C 3.0627532,8.3972503 2.9678314,8.6756875 2.9678210,8.9857618 L 2.9678210,9.0332227"
+       style="font-size:12.000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#ffffff;fill-opacity:1.0000000;stroke:none;stroke-width:1.0000000px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;font-family:Verdana;text-anchor:start;writing-mode:lr-tb" />
+  </g>
+  <g
+     style="display:none"
+     inkscape:label="pan triangle"
+     id="layer3"
+     inkscape:groupmode="layer">
+    <path
+       transform="matrix(0.000000,-1.331000,1.331000,0.000000,2.038879,16.92102)"
+       d="M 9.3471551,9.5937489 L 5.5311469,7.3905753 L 1.7151384,5.1874015 L 5.5311469,2.9842283 L 9.3471555,0.78105489 L 9.3471553,5.1874018 L 9.3471551,9.5937489 z "
+       inkscape:randomized="0.0000000"
+       inkscape:rounded="0.0000000"
+       inkscape:flatsided="false"
+       sodipodi:arg2="2.0943951"
+       sodipodi:arg1="1.0471976"
+       sodipodi:r2="2.5440056"
+       sodipodi:r1="5.0880113"
+       sodipodi:cy="5.1874018"
+       sodipodi:cx="6.8031497"
+       sodipodi:sides="3"
+       id="path2107"
+       style="fill:#ffffff;fill-opacity:1.0000000;stroke:none;stroke-width:39.984146;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000"
+       sodipodi:type="star" />
+  </g>
+</svg>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/layer-switcher-minimize.svg
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/marker.svg
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/marker.svg	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/marker.svg	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 12.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 51448)  -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
+	<!ENTITY ns_ai "http://ns.adobe.com/AdobeIllustrator/10.0/">
+	<!ENTITY ns_svg "http://www.w3.org/2000/svg">
+]>
+<svg  version="1.1" id="Layer_1" xmlns:i="&ns_ai;" 
+	 xmlns="&ns_svg;" width="20.5" height="24.5" viewBox="0 0 20.5 24.5"
+	 overflow="visible" enable-background="new 0 0 20.5 24.5" xml:space="preserve">
+	<g i:extraneous="self">
+		<rect x="0.25" y="0.25" opacity="0" stroke="#000000" stroke-width="1" width="20" height="24"/>
+		<g id="XMLID_2_">
+			<g>
+				<polygon fill="#FF0000" points="10.12,0.6 19.93,7.71 10.06,24.03 0.6,7.58 				"/>
+			</g>
+			<g>
+				<polyline fill="none" stroke="#000000" stroke-width="1" points="9.98,24.16 10.06,24.03 19.93,7.71 20.06,7.49 				"/>
+				<polyline fill="none" stroke="#000000" stroke-width="1" points="10.13,24.16 10.06,24.03 0.6,7.58 0.5,7.41 				"/>
+				<polyline fill="none" stroke="#000000" stroke-width="1" points="0.41,7.72 0.6,7.58 10.12,0.6 10.29,0.47 				"/>
+				<polyline fill="none" stroke="#000000" stroke-width="1" points="9.94,0.47 10.12,0.6 19.93,7.71 20.06,7.8 				"/>
+			</g>
+		</g>
+		<ellipse stroke="#000000" stroke-width="1" cx="10.125" cy="9.25" rx="1.5" ry="1.5"/>
+	</g>
+</svg>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/marker.svg
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/measuring-stick-off.svg
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/measuring-stick-off.svg	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/measuring-stick-off.svg	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 12.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 51448)  -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
+	<!ENTITY ns_extend "http://ns.adobe.com/Extensibility/1.0/">
+	<!ENTITY ns_ai "http://ns.adobe.com/AdobeIllustrator/10.0/">
+	<!ENTITY ns_graphs "http://ns.adobe.com/Graphs/1.0/">
+	<!ENTITY ns_vars "http://ns.adobe.com/Variables/1.0/">
+	<!ENTITY ns_imrep "http://ns.adobe.com/ImageReplacement/1.0/">
+	<!ENTITY ns_sfw "http://ns.adobe.com/SaveForWeb/1.0/">
+	<!ENTITY ns_custom "http://ns.adobe.com/GenericCustomNamespace/1.0/">
+	<!ENTITY ns_adobe_xpath "http://ns.adobe.com/XPath/1.0/">
+	<!ENTITY ns_svg "http://www.w3.org/2000/svg">
+	<!ENTITY ns_xlink "http://www.w3.org/1999/xlink">
+]>
+<svg  version="1.1" id="Layer_1" xmlns:x="&ns_extend;" xmlns:i="&ns_ai;" xmlns:graph="&ns_graphs;"
+	 xmlns="&ns_svg;" xmlns:xlink="&ns_xlink;" width="28.375" height="28" viewBox="0 0 28.375 28"
+	 overflow="visible" enable-background="new 0 0 28.375 28" xml:space="preserve">
+	<g i:extraneous="self">
+		<path fill="none" stroke="#00008B" stroke-width="1.5" d="M27.875,1.375"/>
+		<path fill="none" stroke="#00008B" stroke-width="1.5" d="M0,1.375"/>
+		<path fill="none" stroke="#FFFFFF" d="M0,27.475"/>
+		<path fill="none" stroke="#00008B" stroke-width="1.5" d="M1,27.645"/>
+		<path fill="none" stroke="#00008B" stroke-width="1.5" d="M1,0.544"/>
+		<line fill="none" stroke="#00008B" x1="0.875" y1="27.5" x2="27.875" y2="27.475"/>
+		<rect x="0.875" y="1.045" fill="#00008B" width="27.125" height="26"/>
+		<line fill="none" stroke="#00008B" x1="27.875" y1="26.925" x2="27.875" y2="0.925"/>
+		<line fill="none" stroke="#FFFFFF" x1="1.375" y1="0.5" x2="27.375" y2="0.5"/>
+		<line fill="none" stroke="#FFFFFF" x1="0.875" y1="1" x2="0.875" y2="27"/>
+		
+			<rect x="5.252" y="11.157" transform="matrix(0.7933 -0.6088 0.6088 0.7933 -5.1621 12.1086)" fill="#FFBF00" width="20" height="5"/>
+		<line fill="#FFBF00" stroke="#000000" x1="8.376" y1="15.783" x2="10.717" y2="18.766"/>
+		<line fill="#FFBF00" stroke="#000000" x1="11.946" y1="13.043" x2="14.288" y2="16.027"/>
+		<line fill="#FFBF00" stroke="#000000" x1="15.342" y1="10.437" x2="17.684" y2="13.42"/>
+		<line fill="#FFBF00" stroke="#000000" x1="18.69" y1="7.869" x2="21.03" y2="10.852"/>
+	</g>
+</svg>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/measuring-stick-off.svg
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/measuring-stick-on.svg
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/measuring-stick-on.svg	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/measuring-stick-on.svg	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 12.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 51448)  -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
+	<!ENTITY ns_extend "http://ns.adobe.com/Extensibility/1.0/">
+	<!ENTITY ns_ai "http://ns.adobe.com/AdobeIllustrator/10.0/">
+	<!ENTITY ns_graphs "http://ns.adobe.com/Graphs/1.0/">
+	<!ENTITY ns_vars "http://ns.adobe.com/Variables/1.0/">
+	<!ENTITY ns_imrep "http://ns.adobe.com/ImageReplacement/1.0/">
+	<!ENTITY ns_sfw "http://ns.adobe.com/SaveForWeb/1.0/">
+	<!ENTITY ns_custom "http://ns.adobe.com/GenericCustomNamespace/1.0/">
+	<!ENTITY ns_adobe_xpath "http://ns.adobe.com/XPath/1.0/">
+	<!ENTITY ns_svg "http://www.w3.org/2000/svg">
+	<!ENTITY ns_xlink "http://www.w3.org/1999/xlink">
+]>
+<svg  version="1.1" id="Layer_1" xmlns:x="&ns_extend;" xmlns:i="&ns_ai;" xmlns:graph="&ns_graphs;"
+	 xmlns="&ns_svg;" xmlns:xlink="&ns_xlink;" width="28.375" height="28" viewBox="0 0 28.375 28"
+	 overflow="visible" enable-background="new 0 0 28.375 28" xml:space="preserve">
+	<g i:extraneous="self">
+		<path fill="none" stroke="#00008B" stroke-width="1.5" d="M27.875,1.375"/>
+		<path fill="none" stroke="#00008B" stroke-width="1.5" d="M0,1.375"/>
+		<path fill="none" stroke="#FFFFFF" d="M0,27.475"/>
+		<path fill="none" stroke="#00008B" stroke-width="1.5" d="M1,27.645"/>
+		<path fill="none" stroke="#00008B" stroke-width="1.5" d="M1,0.544"/>
+		<line fill="none" stroke="#FFFFFF" x1="0.875" y1="27.5" x2="27.875" y2="27.475"/>
+		<rect x="0.875" y="1.045" fill="#ADD8E6" width="27.125" height="26"/>
+		<line fill="none" stroke="#FFFFFF" x1="27.875" y1="26.925" x2="27.875" y2="0.925"/>
+		<line fill="none" stroke="#00008B" x1="1.375" y1="0.5" x2="27.375" y2="0.5"/>
+		<line fill="none" stroke="#00008B" x1="0.875" y1="1" x2="0.875" y2="27"/>
+		
+			<rect x="4.252" y="12.157" transform="matrix(0.7933 -0.6088 0.6088 0.7933 -5.9776 11.7065)" fill="#FFBF00" width="20" height="5"/>
+		<line fill="#FFBF00" stroke="#000000" x1="7.376" y1="16.783" x2="9.717" y2="19.766"/>
+		<line fill="#FFBF00" stroke="#000000" x1="10.946" y1="14.043" x2="13.288" y2="17.027"/>
+		<line fill="#FFBF00" stroke="#000000" x1="14.342" y1="11.437" x2="16.684" y2="14.42"/>
+		<line fill="#FFBF00" stroke="#000000" x1="17.69" y1="8.869" x2="20.03" y2="11.852"/>
+	</g>
+</svg>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/measuring-stick-on.svg
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/panning-hand-off.svg
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/panning-hand-off.svg	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/panning-hand-off.svg	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 12.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 51448)  -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
+	<!ENTITY ns_extend "http://ns.adobe.com/Extensibility/1.0/">
+	<!ENTITY ns_ai "http://ns.adobe.com/AdobeIllustrator/10.0/">
+	<!ENTITY ns_graphs "http://ns.adobe.com/Graphs/1.0/">
+	<!ENTITY ns_vars "http://ns.adobe.com/Variables/1.0/">
+	<!ENTITY ns_imrep "http://ns.adobe.com/ImageReplacement/1.0/">
+	<!ENTITY ns_sfw "http://ns.adobe.com/SaveForWeb/1.0/">
+	<!ENTITY ns_custom "http://ns.adobe.com/GenericCustomNamespace/1.0/">
+	<!ENTITY ns_adobe_xpath "http://ns.adobe.com/XPath/1.0/">
+	<!ENTITY ns_svg "http://www.w3.org/2000/svg">
+	<!ENTITY ns_xlink "http://www.w3.org/1999/xlink">
+]>
+<svg  version="1.1" id="Layer_1" xmlns:x="&ns_extend;" xmlns:i="&ns_ai;" xmlns:graph="&ns_graphs;"
+	 xmlns="&ns_svg;" xmlns:xlink="&ns_xlink;" width="28.375" height="28" viewBox="0 0 28.375 28"
+	 overflow="visible" enable-background="new 0 0 28.375 28" xml:space="preserve">
+	<g i:extraneous="self">
+		<path fill="none" stroke="#00008B" stroke-width="1.5" d="M27.875,1.375"/>
+		<path fill="none" stroke="#00008B" stroke-width="1.5" d="M0,1.375"/>
+		<path fill="none" stroke="#FFFFFF" d="M0,27.475"/>
+		<path fill="none" stroke="#00008B" stroke-width="1.5" d="M1,27.645"/>
+		<path fill="none" stroke="#00008B" stroke-width="1.5" d="M1,0.544"/>
+		<line fill="none" stroke="#00008B" x1="0.875" y1="27.5" x2="27.875" y2="27.475"/>
+		<rect x="0.875" y="1.045" fill="#00008B" width="27.125" height="26"/>
+		<line fill="none" stroke="#00008B" x1="27.875" y1="26.925" x2="27.875" y2="0.925"/>
+		<line fill="none" stroke="#FFFFFF" x1="1.375" y1="0.5" x2="27.375" y2="0.5"/>
+		<line fill="none" stroke="#FFFFFF" x1="0.875" y1="1" x2="0.875" y2="27"/>
+		<path fill="#FFFFFF" stroke="#000000" d="M9.458,22.458c-0.473-0.168-1.02-1.269-1.363-1.695
+			c-0.479-0.595-0.894-1.245-1.377-1.829c-0.733-0.887-1.356-1.729-1.854-2.764c-0.375-0.782-0.884-1.997-0.005-2.648
+			c1.036-0.767,2.095-0.162,2.934,0.479c0.844,0.646,1.729,1.18,2.526,1.869c-0.372-0.305-0.548-0.986-0.778-1.421
+			c-0.253-0.477-0.565-0.906-0.822-1.375c-0.359-0.657-0.767-1.201-1.21-1.823c-0.544-0.763-1.051-1.74-0.925-2.709
+			c0.133-1.025,0.972-1.527,1.924-1.207c1.202,0.405,1.969,1.692,2.627,2.698c0.636,0.971,1.201,2.159,1.938,3.042
+			c-0.222-0.209-0.148-0.508-0.155-0.782c-0.01-0.37-0.097-0.626-0.219-0.967c-0.162-0.455-0.219-0.972-0.24-1.45
+			c-0.034-0.776-0.25-1.465-0.25-2.247c0-0.988-0.14-1.963,0.71-2.631c0.666-0.523,1.649-0.764,2.381-0.248
+			c1.385,0.976,1.057,2.999,1.209,4.458c0.079,0.759,0.165,1.489,0.2,2.263c0.022,0.496,0.209,1.067,0.168,1.558
+			c-0.146-0.269-0.113-0.749-0.127-1.057c-0.022-0.502-0.041-0.982-0.115-1.47c-0.114-0.743-0.188-1.564-0.25-2.324
+			c-0.152-1.877,2.054-3.17,3.301-1.469c0.884,1.206,0.773,2.643,0.773,4.085c0,0.698-0.034,1.251-0.22,1.91
+			c-0.13,0.459-0.293,1.222-0.239,1.638c0-0.902,0.334-1.697,0.334-2.588c0-0.678-0.272-2.099,0.581-2.334
+			c1.065-0.294,2.008,0.875,2.428,1.66c0.458,0.855,0.555,1.901,0.409,2.882c-0.318,2.154-0.845,4.227-2.096,6.072
+			c-0.268,0.396-0.485,0.838-0.79,1.22c-0.361,0.452-0.923,0.772-1.416,1.071c-0.381,0.232-0.764,0.53-1.176,0.604
+			c-0.822,0.149-1.725,0.157-2.565,0.157c-1.29,0-2.564-0.068-3.832-0.251c-0.799-0.115-2.12,0.153-2.668-0.54"/>
+	</g>
+</svg>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/panning-hand-off.svg
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/panning-hand-on.svg
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/panning-hand-on.svg	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/panning-hand-on.svg	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 12.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 51448)  -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
+	<!ENTITY ns_extend "http://ns.adobe.com/Extensibility/1.0/">
+	<!ENTITY ns_ai "http://ns.adobe.com/AdobeIllustrator/10.0/">
+	<!ENTITY ns_graphs "http://ns.adobe.com/Graphs/1.0/">
+	<!ENTITY ns_vars "http://ns.adobe.com/Variables/1.0/">
+	<!ENTITY ns_imrep "http://ns.adobe.com/ImageReplacement/1.0/">
+	<!ENTITY ns_sfw "http://ns.adobe.com/SaveForWeb/1.0/">
+	<!ENTITY ns_custom "http://ns.adobe.com/GenericCustomNamespace/1.0/">
+	<!ENTITY ns_adobe_xpath "http://ns.adobe.com/XPath/1.0/">
+	<!ENTITY ns_svg "http://www.w3.org/2000/svg">
+	<!ENTITY ns_xlink "http://www.w3.org/1999/xlink">
+]>
+<svg  version="1.1" id="Layer_1" xmlns:x="&ns_extend;" xmlns:i="&ns_ai;" xmlns:graph="&ns_graphs;"
+	 xmlns="&ns_svg;" xmlns:xlink="&ns_xlink;" width="28.375" height="28" viewBox="0 0 28.375 28"
+	 overflow="visible" enable-background="new 0 0 28.375 28" xml:space="preserve">
+	<g i:extraneous="self">
+		<path fill="none" stroke="#00008B" stroke-width="1.5" d="M27.875,1.375"/>
+		<path fill="none" stroke="#00008B" stroke-width="1.5" d="M0,1.375"/>
+		<path fill="none" stroke="#FFFFFF" d="M0,27.475"/>
+		<path fill="none" stroke="#00008B" stroke-width="1.5" d="M1,27.645"/>
+		<path fill="none" stroke="#00008B" stroke-width="1.5" d="M1,0.544"/>
+		<line fill="none" stroke="#FFFFFF" x1="0.875" y1="27.5" x2="27.875" y2="27.475"/>
+		<rect x="0.875" y="1.045" fill="#ADD8E6" width="27.125" height="26"/>
+		<line fill="none" stroke="#FFFFFF" x1="27.875" y1="26.925" x2="27.875" y2="0.925"/>
+		<line fill="none" stroke="#00008B" x1="1.375" y1="0.5" x2="27.375" y2="0.5"/>
+		<line fill="none" stroke="#00008B" x1="0.875" y1="1" x2="0.875" y2="27"/>
+		<path fill="#FFFFFF" stroke="#000000" d="M8.458,23.458c-0.473-0.168-1.02-1.269-1.363-1.695
+			c-0.479-0.595-0.894-1.245-1.377-1.829c-0.733-0.887-1.356-1.729-1.854-2.764c-0.375-0.782-0.884-1.997-0.005-2.648
+			c1.036-0.767,2.095-0.161,2.934,0.479c0.844,0.646,1.729,1.18,2.526,1.869c-0.372-0.305-0.548-0.986-0.778-1.421
+			c-0.253-0.477-0.565-0.906-0.822-1.375c-0.359-0.657-0.767-1.201-1.21-1.823c-0.544-0.763-1.051-1.74-0.925-2.709
+			c0.133-1.025,0.972-1.527,1.924-1.207c1.202,0.405,1.969,1.692,2.627,2.698c0.636,0.971,1.201,2.159,1.938,3.042
+			c-0.222-0.21-0.148-0.509-0.155-0.783c-0.01-0.37-0.097-0.626-0.219-0.967c-0.162-0.455-0.219-0.972-0.24-1.45
+			c-0.034-0.776-0.25-1.465-0.25-2.247c0-0.988-0.14-1.963,0.71-2.631c0.666-0.523,1.649-0.764,2.381-0.248
+			c1.385,0.976,1.057,2.999,1.209,4.458c0.079,0.759,0.165,1.489,0.2,2.263c0.022,0.496,0.209,1.068,0.168,1.558
+			c-0.146-0.27-0.113-0.75-0.127-1.058c-0.022-0.502-0.041-0.982-0.115-1.47c-0.114-0.743-0.188-1.564-0.25-2.324
+			c-0.152-1.877,2.054-3.17,3.301-1.469c0.884,1.206,0.773,2.643,0.773,4.085c0,0.698-0.034,1.251-0.22,1.91
+			c-0.13,0.459-0.293,1.222-0.239,1.639c0-0.902,0.334-1.697,0.334-2.589c0-0.678-0.272-2.099,0.581-2.334
+			c1.065-0.294,2.008,0.875,2.428,1.66c0.458,0.855,0.555,1.901,0.409,2.883c-0.318,2.153-0.845,4.227-2.096,6.071
+			c-0.268,0.396-0.485,0.838-0.79,1.22c-0.361,0.452-0.923,0.772-1.416,1.071c-0.381,0.232-0.764,0.53-1.176,0.604
+			c-0.822,0.149-1.725,0.157-2.565,0.157c-1.29,0-2.564-0.068-3.832-0.251c-0.799-0.115-2.12,0.153-2.668-0.54"/>
+	</g>
+</svg>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/panning-hand-on.svg
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/slider.svg
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/slider.svg	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/slider.svg	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://inkscape.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="210mm"
+   height="297mm"
+   id="svg2"
+   sodipodi:version="0.32"
+   inkscape:version="0.41.1"
+   sodipodi:docbase="/afs/metacarta.com/user/sderle/public_html/ol.zoombar/art"
+   sodipodi:docname="slider.svg"
+   inkscape:export-filename="/afs/metacarta.com/user/sderle/public_html/ol.zoombar/img/slider.png"
+   inkscape:export-xdpi="90.000000"
+   inkscape:export-ydpi="90.000000">
+  <defs
+     id="defs3" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="39.870978"
+     inkscape:cx="140.00732"
+     inkscape:cy="695.49796"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:window-width="1010"
+     inkscape:window-height="538"
+     inkscape:window-x="20"
+     inkscape:window-y="107" />
+  <metadata
+     id="metadata4">
+    <rdf:RDF
+       id="RDF5">
+      <cc:Work
+         rdf:about=""
+         id="Work6">
+        <dc:format
+           id="format7">image/svg+xml</dc:format>
+        <dc:type
+           id="type9"
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1">
+    <rect
+       style="fill:#00008b;fill-opacity:1.0000000;stroke:#000080;stroke-width:4.9999957;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000"
+       id="rect1291"
+       x="132.50000"
+       y="354.86218"
+       width="15.014628"
+       height="4.0040474" />
+    <path
+       style="fill:none;fill-opacity:0.75000000;fill-rule:evenodd;stroke:#ffffff;stroke-width:2.0000000;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000"
+       d="M 134.08880,356.89603 L 146.00000,356.87002"
+       id="path2052" />
+  </g>
+</svg>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/slider.svg
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/zoom-world.svg
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/zoom-world.svg	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/zoom-world.svg	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,193 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:xml="http://www.w3.org/XML/1998/namespace"
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://inkscape.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   inkscape:export-ydpi="90.000000"
+   inkscape:export-xdpi="90.000000"
+   inkscape:export-filename="/Users/phil/Documents/work/metacarta/2svn.openlayers.net/follower/code/b-edits-1/openlayers/assets/ui_elements/zoom-minus-mini.png"
+   sodipodi:docname="zoom-world.svg"
+   sodipodi:docbase="/afs/metacarta.com/user/sderle/OL/openlayers/assets/ui_elements"
+   inkscape:version="0.41.1"
+   sodipodi:version="0.32"
+   id="svg2"
+   height="18.000000px"
+   width="18.000000px">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     inkscape:window-y="26"
+     inkscape:window-x="42"
+     inkscape:window-height="800"
+     inkscape:window-width="1226"
+     inkscape:current-layer="layer5"
+     inkscape:document-units="px"
+     inkscape:cy="9.3025513"
+     inkscape:cx="9.0000000"
+     inkscape:zoom="1.0000000"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     borderopacity="1.0"
+     bordercolor="#666666"
+     pagecolor="#ffffff"
+     id="base" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF
+       id="RDF1295">
+      <cc:Work
+         id="Work1297"
+         rdf:about="">
+        <dc:format
+           id="format1299">image/svg+xml</dc:format>
+        <dc:type
+           id="type1301"
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     style="display:inline"
+     id="layer1"
+     inkscape:groupmode="layer"
+     inkscape:label="background">
+    <rect
+       style="fill:#00008b;fill-opacity:1.0000000;stroke:none;stroke-width:39.984146;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000"
+       id="rect2061"
+       width="18.014498"
+       height="18.000000"
+       x="-18.014502"
+       y="4.3599998e-06"
+       transform="matrix(0.000000,-1.000000,1.000000,0.000000,0.000000,0.000000)" />
+  </g>
+  <g
+     inkscape:label="graticule"
+     id="layer5"
+     inkscape:groupmode="layer">
+    <g
+       transform="translate(0.000000,-4.419417e-2)"
+       id="g2876">
+      <path
+         transform="matrix(1.155321,0.000000,0.000000,1.155321,-2.292065,-1.751761)"
+         d="M 16.869573 9.3188362 A 7.0579743 7.0579743 0 1 1  2.7536244,9.3188362 A 7.0579743 7.0579743 0 1 1  16.869573 9.3188362 z"
+         sodipodi:ry="7.0579743"
+         sodipodi:rx="7.0579743"
+         sodipodi:cy="9.3188362"
+         sodipodi:cx="9.8115988"
+         id="path1321"
+         style="fill:none;fill-opacity:1.0000000;stroke:#ffffff;stroke-width:0.50000000;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000"
+         sodipodi:type="arc" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path2086"
+         d="M 8.1145142,1.1289949 C 15.315267,9.5238831 8.3963197,17.101449 8.3963197,17.057255"
+         style="fill:none;fill-opacity:0.75000000;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.50000000;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path2846"
+         d="M 11.588885,1.3666059 C 18.922222,9.5405232 11.605526,16.764536 11.605526,16.764536"
+         style="fill:none;fill-opacity:0.75000000;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.50000000;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path2848"
+         d="M 5.2258471,2.0593951 C 11.080923,9.7405585 5.3432210,16.278941 5.3432210,16.278941"
+         style="fill:none;fill-opacity:0.75000000;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.50000000;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path2850"
+         d="M 2.7388198,3.7571959 C 6.3686281,9.1817837 2.9127331,14.361601 2.9127331,14.361601"
+         style="fill:none;fill-opacity:0.75000000;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.50000000;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000" />
+    </g>
+    <g
+       transform="matrix(-6.269315e-2,0.998033,-0.998033,-6.269315e-2,18.62352,0.612232)"
+       id="g2883">
+      <path
+         transform="matrix(1.155321,0.000000,0.000000,1.155321,-2.292065,-1.751761)"
+         d="M 16.869573 9.3188362 A 7.0579743 7.0579743 0 1 1  2.7536244,9.3188362 A 7.0579743 7.0579743 0 1 1  16.869573 9.3188362 z"
+         sodipodi:ry="7.0579743"
+         sodipodi:rx="7.0579743"
+         sodipodi:cy="9.3188362"
+         sodipodi:cx="9.8115988"
+         id="path2885"
+         style="fill:none;fill-opacity:1.0000000;stroke:#ffffff;stroke-width:0.50000000;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000"
+         sodipodi:type="arc" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path2887"
+         d="M 8.1145142,1.1289949 C 15.315267,9.5238831 8.3963197,17.101449 8.3963197,17.057255"
+         style="fill:none;fill-opacity:0.75000000;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.50000000;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path2889"
+         d="M 11.588885,1.3666059 C 18.922222,9.5405232 11.605526,16.764536 11.605526,16.764536"
+         style="fill:none;fill-opacity:0.75000000;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.50000000;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path2891"
+         d="M 5.2258471,2.0593951 C 11.080923,9.7405585 5.3432210,16.278941 5.3432210,16.278941"
+         style="fill:none;fill-opacity:0.75000000;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.50000000;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path2893"
+         d="M 2.7388198,3.7571959 C 6.3686281,9.1817837 2.9127331,14.361601 2.9127331,14.361601"
+         style="fill:none;fill-opacity:0.75000000;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.50000000;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000" />
+    </g>
+  </g>
+  <g
+     style="display:none"
+     inkscape:label="zoom"
+     id="layer4"
+     inkscape:groupmode="layer">
+    <text
+       transform="scale(1.000000,-1.000000)"
+       sodipodi:linespacing="125.00000%"
+       id="text2114"
+       y="-3.4077148"
+       x="3.7485352"
+       style="font-size:18.000000;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;fill:#ffffff;fill-opacity:1.0000000;stroke:#ffffff;stroke-width:1.0000000px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1.0000000;font-family:Helvetica;text-anchor:start;writing-mode:lr-tb"
+       xml:space="preserve"><tspan
+         y="-3.4077148"
+         x="3.7485352"
+         id="tspan2118"
+         sodipodi:role="line">−</tspan></text>
+  </g>
+  <g
+     style="display:none"
+     inkscape:label="overlay"
+     id="layer2"
+     inkscape:groupmode="layer">
+    <path
+       sodipodi:nodetypes="csccsssscccssscccssssccscc"
+       id="text2082"
+       d="M 2.9678210,9.0332227 C 2.9678314,9.5015070 3.1752531,9.8938504 3.5900866,10.210254 C 3.6463464,10.254553 3.9451742,10.453889 4.4865710,10.808262 L 8.1568835,13.247754 C 8.2693886,13.323691 8.4240759,13.393300 8.6209460,13.456582 C 8.8107943,13.519862 9.0041535,13.551503 9.2010240,13.551504 C 9.5736843,13.551503 9.8936056,13.431269 10.160789,13.190801 C 10.427980,12.950332 10.561574,12.659238 10.561572,12.317520 C 10.561574,11.861896 10.322511,11.488536 9.8443836,11.197442 L 8.5998522,10.428575 L 14.548408,10.428575 C 15.026535,10.428577 15.406221,10.298849 15.687470,10.039395 C 15.968721,9.7799443 16.109345,9.4318977 16.109345,8.9952540 C 16.109345,8.5396330 15.961690,8.1915865 15.666377,7.9511133 C 15.364034,7.7106494 14.980831,7.5904152 14.516767,7.5904102 L 8.6314928,7.5904102 L 9.9920390,6.7076368 C 10.160793,6.5937365 10.290871,6.4481897 10.382274,6.2709962 C 10.466652,6.0874870 10.508840,5.8913153 10.508837,5.6824805 C 10.508840,5.3281127 10.378761,5.0338552 10.118602,4.7997070 C 9.8584498,4.5655745 9.5279814,4.4485043 9.1271961,4.4484962 C 8.7615757,4.4485043 8.4029823,4.5624104 8.0514147,4.7902149 L 3.9697741,7.5239649 C 3.6393151,7.7454540 3.4002529,7.9542820 3.2525866,8.1504493 C 3.0627532,8.3972503 2.9678314,8.6756875 2.9678210,8.9857618 L 2.9678210,9.0332227"
+       style="font-size:12.000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#ffffff;fill-opacity:1.0000000;stroke:none;stroke-width:1.0000000px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;font-family:Verdana;text-anchor:start;writing-mode:lr-tb" />
+  </g>
+  <g
+     style="display:none"
+     inkscape:label="pan triangle"
+     id="layer3"
+     inkscape:groupmode="layer">
+    <path
+       transform="matrix(0.000000,-1.331000,1.331000,0.000000,2.038879,16.92102)"
+       d="M 9.3471551,9.5937489 L 5.5311469,7.3905753 L 1.7151384,5.1874015 L 5.5311469,2.9842283 L 9.3471555,0.78105489 L 9.3471553,5.1874018 L 9.3471551,9.5937489 z "
+       inkscape:randomized="0.0000000"
+       inkscape:rounded="0.0000000"
+       inkscape:flatsided="false"
+       sodipodi:arg2="2.0943951"
+       sodipodi:arg1="1.0471976"
+       sodipodi:r2="2.5440056"
+       sodipodi:r1="5.0880113"
+       sodipodi:cy="5.1874018"
+       sodipodi:cx="6.8031497"
+       sodipodi:sides="3"
+       id="path2107"
+       style="fill:#ffffff;fill-opacity:1.0000000;stroke:none;stroke-width:39.984146;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000"
+       sodipodi:type="star" />
+  </g>
+</svg>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/zoom-world.svg
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/zoombar.svg
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/zoombar.svg	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/zoombar.svg	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://inkscape.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="18"
+   height="18"
+   id="svg2"
+   sodipodi:version="0.32"
+   inkscape:version="0.43"
+   version="1.0"
+   inkscape:export-filename="C:\Documents and Settings\crschmidt\Desktop\zoombar.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90"
+   sodipodi:docbase="C:\Documents and Settings\crschmidt\Desktop"
+   sodipodi:docname="zoombar.svg">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1"
+     inkscape:cx="-117.50597"
+     inkscape:cy="65.94562"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showguides="false"
+     showgrid="false"
+     inkscape:window-width="1024"
+     inkscape:window-height="721"
+     inkscape:window-x="-4"
+     inkscape:window-y="-4" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1">
+    <rect
+       style="opacity:0.5;fill:#808080;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.00000012;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="rect1307"
+       width="16.999998"
+       height="10.000004"
+       x="-125.51302"
+       y="-52.420002" />
+    <path
+       style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:0.99999958;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.50196078"
+       d="M -125.54176,-52.415494 L -125.54176,-42.415495"
+       id="path8297" />
+    <path
+       style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:0.99999982;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.50196078"
+       d="M -108.48157,-52.418044 L -108.48157,-42.418046"
+       id="path12667" />
+  </g>
+</svg>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/art/zoombar.svg
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/build/OpenLayers.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/build/OpenLayers.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/build/OpenLayers.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,503 @@
+/*
+
+  OpenLayers.js -- OpenLayers Map Viewer Library
+
+  Copyright 2005-2006 MetaCarta, Inc., released under a modified BSD license.
+  Please see http://svn.openlayers.org/trunk/openlayers/release-license.txt
+  for the full text of the license.
+
+  Includes compressed code under the following licenses:
+
+  (For uncompressed versions of the code used please see the
+  OpenLayers SVN repository: <http://openlayers.org/>)
+
+*/
+
+/* Contains portions of Prototype.js:
+ *
+ * Prototype JavaScript framework, version 1.4.0
+ *  (c) 2005 Sam Stephenson <sam at conio.net>
+ *
+ *  Prototype is freely distributable under the terms of an MIT-style license.
+ *  For details, see the Prototype web site: http://prototype.conio.net/
+ *
+/*--------------------------------------------------------------------------*/
+
+/**  
+*  
+*  Contains portions of Rico <http://openrico.org/>
+* 
+*  Copyright 2005 Sabre Airline Solutions  
+*  
+*  Licensed under the Apache License, Version 2.0 (the "License"); you
+*  may not use this file except in compliance with the License. You
+*  may obtain a copy of the License at
+*  
+*         http://www.apache.org/licenses/LICENSE-2.0  
+*  
+*  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. 
+*
+**/
+
+
+_OPENLAYERS_SFL_=true
+OpenLayers=new Object();OpenLayers._scriptName=(typeof(_OPENLAYERS_SFL_)=="undefined"?"lib/OpenLayers.js":"OpenLayers.js");OpenLayers._getScriptLocation=function(){var scriptLocation="";var SCRIPT_NAME=OpenLayers._scriptName;var scripts=document.getElementsByTagName('script');for(var i=0;i<scripts.length;i++){var src=scripts[i].getAttribute('src');if(src){var index=src.lastIndexOf(SCRIPT_NAME);if((index>-1)&&(index+SCRIPT_NAME.length==src.length)){scriptLocation=src.slice(0,-SCRIPT_NAME.length);break;}}}
+return scriptLocation;}
+if(typeof(_OPENLAYERS_SFL_)=="undefined"){(function(){var jsfiles=new Array("OpenLayers/BaseTypes.js","OpenLayers/Util.js","Rico/Corner.js","Rico/Color.js","OpenLayers/Ajax.js","OpenLayers/Events.js","OpenLayers/Map.js","OpenLayers/Layer.js","OpenLayers/Icon.js","OpenLayers/Marker.js","OpenLayers/Marker/Box.js","OpenLayers/Popup.js","OpenLayers/Tile.js","OpenLayers/Feature.js","OpenLayers/Feature/WFS.js","OpenLayers/Tile/Image.js","OpenLayers/Tile/WFS.js","OpenLayers/Layer/Image.js","OpenLayers/Layer/EventPane.js","OpenLayers/Layer/FixedZoomLevels.js","OpenLayers/Layer/Google.js","OpenLayers/Layer/VirtualEarth.js","OpenLayers/Layer/Yahoo.js","OpenLayers/Layer/HTTPRequest.js","OpenLayers/Layer/Grid.js","OpenLayers/Layer/MapServer.js","OpenLayers/Layer/KaMap.js","OpenLayers/Layer/MultiMap.js","OpenLayers/Layer/Markers.js","OpenLayers/Layer/Text.js","OpenLayers/Layer/WorldWind.js","OpenLayers/Layer/WMS.js","OpenLayers/Layer/WFS.js","OpenLayers/Layer/WMS/Untiled.js","OpenLayers/Layer/GeoRSS.js","OpenLayers/Layer/Boxes.js","OpenLayers/Layer/Canvas.js","OpenLayers/Popup/Anchored.js","OpenLayers/Popup/AnchoredBubble.js","OpenLayers/Control.js","OpenLayers/Control/MouseDefaults.js","OpenLayers/Control/MouseToolbar.js","OpenLayers/Control/MousePosition.js","OpenLayers/Control/OverviewMap.js","OpenLayers/Control/KeyboardDefaults.js","OpenLayers/Control/PanZoom.js","OpenLayers/Control/PanZoomBar.js","OpenLayers/Control/ArgParser.js","OpenLayers/Control/Permalink.js","OpenLayers/Control/Scale.js","OpenLayers/Control/LayerSwitcher.js");var allScriptTags="";var host=OpenLayers._getScriptLocation()+"lib/";for(var i=0;i<jsfiles.length;i++){if(/MSIE/.test(navigator.userAgent)||/Safari/.test(navigator.userAgent)){var currentScriptTag="<script src='"+host+jsfiles[i]+"'></script>";allScriptTags+=currentScriptTag;}else{var s=document.createElement("script");s.src=host+jsfiles[i];var h=document.getElementsByTagName("head").length?document.getElementsByTagName("head")[0]:document.body;h.appendChild(s);}}
+if(allScriptTags)document.write(allScriptTags);})();}
+OpenLayers.VERSION_NUMBER="$Revision: 1865 $";OpenLayers.Class={isPrototype:function(){},create:function(){return function(){if(arguments&&arguments[0]!=OpenLayers.Class.isPrototype)
+this.initialize.apply(this,arguments);}},inherit:function(){var superClass=arguments[0];var proto=new superClass(OpenLayers.Class.isPrototype);for(var i=1;i<arguments.length;i++){if(typeof arguments[i]=="function"){var mixin=arguments[i];arguments[i]=new mixin(OpenLayers.Class.isPrototype);}
+OpenLayers.Util.extend(proto,arguments[i]);}
+return proto;}};OpenLayers.Pixel=OpenLayers.Class.create();OpenLayers.Pixel.prototype={x:0.0,y:0.0,initialize:function(x,y){this.x=parseFloat(x);this.y=parseFloat(y);},toString:function(){return("x="+this.x+",y="+this.y);},clone:function(){return new OpenLayers.Pixel(this.x,this.y);},equals:function(px){var equals=false;if(px!=null){equals=((this.x==px.x&&this.y==px.y)||(isNaN(this.x)&&isNaN(this.y)&&isNaN(px.x)&&isNaN(px.y)));}
+return equals;},add:function(x,y){return new OpenLayers.Pixel(this.x+x,this.y+y);},offset:function(px){var newPx=this.clone();if(px){newPx=this.add(px.x,px.y);}
+return newPx;},CLASS_NAME:"OpenLayers.Pixel"};OpenLayers.Size=OpenLayers.Class.create();OpenLayers.Size.prototype={w:0.0,h:0.0,initialize:function(w,h){this.w=parseFloat(w);this.h=parseFloat(h);},toString:function(){return("w="+this.w+",h="+this.h);},clone:function(){return new OpenLayers.Size(this.w,this.h);},equals:function(sz){var equals=false;if(sz!=null){equals=((this.w==sz.w&&this.h==sz.h)||(isNaN(this.w)&&isNaN(this.h)&&isNaN(sz.w)&&isNaN(sz.h)));}
+return equals;},CLASS_NAME:"OpenLayers.Size"};OpenLayers.LonLat=OpenLayers.Class.create();OpenLayers.LonLat.prototype={lon:0.0,lat:0.0,initialize:function(lon,lat){this.lon=parseFloat(lon);this.lat=parseFloat(lat);},toString:function(){return("lon="+this.lon+",lat="+this.lat);},toShortString:function(){return(this.lon+", "+this.lat);},clone:function(){return new OpenLayers.LonLat(this.lon,this.lat);},add:function(lon,lat){return new OpenLayers.LonLat(this.lon+lon,this.lat+lat);},equals:function(ll){var equals=false;if(ll!=null){equals=((this.lon==ll.lon&&this.lat==ll.lat)||(isNaN(this.lon)&&isNaN(this.lat)&&isNaN(ll.lon)&&isNaN(ll.lat)));}
+return equals;},CLASS_NAME:"OpenLayers.LonLat"};OpenLayers.LonLat.fromString=function(str){var pair=str.split(",");return new OpenLayers.LonLat(parseFloat(pair[0]),parseFloat(pair[1]));};OpenLayers.Bounds=OpenLayers.Class.create();OpenLayers.Bounds.prototype={left:0.0,bottom:0.0,right:0.0,top:0.0,initialize:function(left,bottom,right,top){this.left=parseFloat(left);this.bottom=parseFloat(bottom);this.right=parseFloat(right);this.top=parseFloat(top);},clone:function(){return new OpenLayers.Bounds(this.left,this.bottom,this.right,this.top);},equals:function(bounds){var equals=false;if(bounds!=null){equals=((this.left==bounds.left)&&(this.right==bounds.right)&&(this.top==bounds.top)&&(this.bottom==bounds.bottom));}
+return equals;},toString:function(){return("left-bottom=("+this.left+","+this.bottom+")"
++" right-top=("+this.right+","+this.top+")");},toBBOX:function(decimal){if(decimal==null){decimal=6;}
+var mult=Math.pow(10,decimal);var bbox=Math.round(this.left*mult)/mult+","+
+Math.round(this.bottom*mult)/mult+","+
+Math.round(this.right*mult)/mult+","+
+Math.round(this.top*mult)/mult;return bbox;},getWidth:function(){return(this.right-this.left);},getHeight:function(){return(this.top-this.bottom);},getSize:function(){return new OpenLayers.Size(this.getWidth(),this.getHeight());},getCenterPixel:function(){return new OpenLayers.Pixel((this.left+this.right)/2,(this.bottom+this.top)/2);},getCenterLonLat:function(){return new OpenLayers.LonLat((this.left+this.right)/2,(this.bottom+this.top)/2);},add:function(x,y){return new OpenLayers.Bounds(this.left+x,this.bottom+y,this.right+x,this.top+y);},containsLonLat:function(ll,inclusive){return this.contains(ll.lon,ll.lat,inclusive);},containsPixel:function(px,inclusive){return this.contains(px.x,px.y,inclusive);},contains:function(x,y,inclusive){if(inclusive==null){inclusive=true;}
+var contains=false;if(inclusive){contains=((x>=this.left)&&(x<=this.right)&&(y>=this.bottom)&&(y<=this.top));}else{contains=((x>this.left)&&(x<this.right)&&(y>this.bottom)&&(y<this.top));}
+return contains;},intersectsBounds:function(bounds,inclusive){if(inclusive==null){inclusive=true;}
+var inBottom=(bounds.bottom==this.bottom&&bounds.top==this.top)?true:(((bounds.bottom>this.bottom)&&(bounds.bottom<this.top))||((this.bottom>bounds.bottom)&&(this.bottom<bounds.top)));var inTop=(bounds.bottom==this.bottom&&bounds.top==this.top)?true:(((bounds.top>this.bottom)&&(bounds.top<this.top))||((this.top>bounds.bottom)&&(this.top<bounds.top)));var inRight=(bounds.right==this.right&&bounds.left==this.left)?true:(((bounds.right>this.left)&&(bounds.right<this.right))||((this.right>bounds.left)&&(this.right<bounds.right)));var inLeft=(bounds.right==this.right&&bounds.left==this.left)?true:(((bounds.left>this.left)&&(bounds.left<this.right))||((this.left>bounds.left)&&(this.left<bounds.right)));return(this.containsBounds(bounds,true,inclusive)||bounds.containsBounds(this,true,inclusive)||((inTop||inBottom)&&(inLeft||inRight)));},containsBounds:function(bounds,partial,inclusive){if(partial==null){partial=false;}
+if(inclusive==null){inclusive=true;}
+var inLeft;var inTop;var inRight;var inBottom;if(inclusive){inLeft=(bounds.left>=this.left)&&(bounds.left<=this.right);inTop=(bounds.top>=this.bottom)&&(bounds.top<=this.top);inRight=(bounds.right>=this.left)&&(bounds.right<=this.right);inBottom=(bounds.bottom>=this.bottom)&&(bounds.bottom<=this.top);}else{inLeft=(bounds.left>this.left)&&(bounds.left<this.right);inTop=(bounds.top>this.bottom)&&(bounds.top<this.top);inRight=(bounds.right>this.left)&&(bounds.right<this.right);inBottom=(bounds.bottom>this.bottom)&&(bounds.bottom<this.top);}
+return(partial)?(inTop||inBottom)&&(inLeft||inRight):(inTop&&inLeft&&inBottom&&inRight);},determineQuadrant:function(lonlat){var quadrant="";var center=this.getCenterLonLat();quadrant+=(lonlat.lat<center.lat)?"b":"t";quadrant+=(lonlat.lon<center.lon)?"l":"r";return quadrant;},CLASS_NAME:"OpenLayers.Bounds"};OpenLayers.Bounds.fromString=function(str){var bounds=str.split(",");return OpenLayers.Bounds.fromArray(bounds);};OpenLayers.Bounds.fromArray=function(bbox){return new OpenLayers.Bounds(parseFloat(bbox[0]),parseFloat(bbox[1]),parseFloat(bbox[2]),parseFloat(bbox[3]));};OpenLayers.Bounds.fromSize=function(size){return new OpenLayers.Bounds(0,size.h,size.w,0);};OpenLayers.Bounds.oppositeQuadrant=function(quadrant){var opp="";opp+=(quadrant.charAt(0)=='t')?'b':'t';opp+=(quadrant.charAt(1)=='l')?'r':'l';return opp;};OpenLayers.Element={visible:function(element){return $(element).style.display!='none';},toggle:function(){for(var i=0;i<arguments.length;i++){var element=$(arguments[i]);OpenLayers.Element[OpenLayers.Element.visible(element)?'hide':'show'](element);}},hide:function(){for(var i=0;i<arguments.length;i++){var element=$(arguments[i]);element.style.display='none';}},show:function(){for(var i=0;i<arguments.length;i++){var element=$(arguments[i]);element.style.display='';}},remove:function(element){element=$(element);element.parentNode.removeChild(element);},getHeight:function(element){element=$(element);return element.offsetHeight;},getDimensions:function(element){element=$(element);if(OpenLayers.Element.getStyle(element,'display')!='none')
+return{width:element.offsetWidth,height:element.offsetHeight};var els=element.style;var originalVisibility=els.visibility;var originalPosition=els.position;els.visibility='hidden';els.position='absolute';els.display='';var originalWidth=element.clientWidth;var originalHeight=element.clientHeight;els.display='none';els.position=originalPosition;els.visibility=originalVisibility;return{width:originalWidth,height:originalHeight};},getStyle:function(element,style){element=$(element);var value=element.style[style.camelize()];if(!value){if(document.defaultView&&document.defaultView.getComputedStyle){var css=document.defaultView.getComputedStyle(element,null);value=css?css.getPropertyValue(style):null;}else if(element.currentStyle){value=element.currentStyle[style.camelize()];}}
+if(window.opera&&OpenLayers.Util.indexOf(['left','top','right','bottom'],style)!=-1)
+if(OpenLayers.Element.getStyle(element,'position')=='static')value='auto';return value=='auto'?null:value;}};String.prototype.startsWith=function(sStart){return(this.substr(0,sStart.length)==sStart);};String.prototype.contains=function(str){return(this.indexOf(str)!=-1);};String.prototype.trim=function(){var b=0;while(this.substr(b,1)==" "){b++;}
+var e=this.length-1;while(this.substr(e,1)==" "){e--;}
+return this.substring(b,e+1);};String.indexOf=function(object){for(var i=0;i<this.length;i++)
+if(this[i]==object)return i;return-1;};String.prototype.camelize=function(){var oStringList=this.split('-');if(oStringList.length==1)return oStringList[0];var camelizedString=this.indexOf('-')==0?oStringList[0].charAt(0).toUpperCase()+oStringList[0].substring(1):oStringList[0];for(var i=1,len=oStringList.length;i<len;i++){var s=oStringList[i];camelizedString+=s.charAt(0).toUpperCase()+s.substring(1);}
+return camelizedString;};Number.prototype.limitSigDigs=function(sig){var number=(sig>0)?this.toString():0;if(sig<number.length){var exp=number.length-sig;number=Math.round(this/Math.pow(10,exp))*Math.pow(10,exp);}
+return parseInt(number);}
+Function.prototype.bind=function(){var __method=this,args=[],object=arguments[0];for(var i=1;i<arguments.length;i++)
+args.push(arguments[i]);return function(moreargs){for(var i=0;i<arguments.length;i++)
+args.push(arguments[i]);return __method.apply(object,args);}};Function.prototype.bindAsEventListener=function(object){var __method=this;return function(event){return __method.call(object,event||window.event);}};OpenLayers.Util=new Object();if($==null){var $=function(){var elements=new Array();for(var i=0;i<arguments.length;i++){var element=arguments[i];if(typeof element=='string')
+element=document.getElementById(element);if(arguments.length==1)
+return element;elements.push(element);}
+return elements;}}
+OpenLayers.Util.extend=function(destination,source){for(property in source){destination[property]=source[property];}
+return destination;};OpenLayers.Util.removeItem=function(array,item){for(var i=0;i<array.length;i++){if(array[i]==item){array.splice(i,1);}}
+return array;};OpenLayers.Util.clearArray=function(array){array.length=0;};OpenLayers.Util.indexOf=function(array,obj){for(var i=0;i<array.length;i++){if(array[i]==obj)return i;}
+return-1;};OpenLayers.Util.modifyDOMElement=function(element,id,px,sz,position,border,overflow,opacity){if(id){element.id=id;}
+if(px){element.style.left=px.x+"px";element.style.top=px.y+"px";}
+if(sz){element.style.width=sz.w+"px";element.style.height=sz.h+"px";}
+if(position){element.style.position=position;}
+if(border){element.style.border=border;}
+if(overflow){element.style.overflow=overflow;}
+if(opacity){element.style.opacity=opacity;element.style.filter='alpha(opacity='+(opacity*100)+')';}};OpenLayers.Util.createDiv=function(id,px,sz,imgURL,position,border,overflow,opacity){var dom=document.createElement('div');if(imgURL){dom.style.backgroundImage='url('+imgURL+')';}
+if(!id){id=OpenLayers.Util.createUniqueID("OpenLayersDiv");}
+if(!position){position="absolute";}
+OpenLayers.Util.modifyDOMElement(dom,id,px,sz,position,border,overflow,opacity);return dom;};OpenLayers.Util.createImage=function(id,px,sz,imgURL,position,border,opacity,delayDisplay){image=document.createElement("img");if(delayDisplay){image.style.display="none";OpenLayers.Event.observe(image,"load",OpenLayers.Util.onImageLoad.bindAsEventListener(image));OpenLayers.Event.observe(image,"error",OpenLayers.Util.onImageLoadError.bindAsEventListener(image));}
+image.style.alt=id;image.galleryImg="no";if(imgURL){image.src=imgURL;}
+if(!id){id=OpenLayers.Util.createUniqueID("OpenLayersDiv");}
+if(!position){position="relative";}
+OpenLayers.Util.modifyDOMElement(image,id,px,sz,position,border,null,opacity);return image;};OpenLayers.Util.setOpacity=function(element,opacity){OpenLayers.Util.modifyDOMElement(element,null,null,null,null,null,null,opacity);}
+OpenLayers.Util.onImageLoad=function(){this.style.backgroundColor=null;this.style.display="";};OpenLayers.Util.onImageLoadErrorColor="pink";OpenLayers.Util.onImageLoadError=function(){this.style.backgroundColor=OpenLayers.Util.onImageLoadErrorColor;this.style.display="";};OpenLayers.Util.alphaHack=function(){var arVersion=navigator.appVersion.split("MSIE");var version=parseFloat(arVersion[1]);var filter=false;try{filter=document.body.filters;}catch(e){}
+return(filter&&(version>=5.5)&&(version<7));}
+OpenLayers.Util.modifyAlphaImageDiv=function(div,id,px,sz,imgURL,position,border,sizing,opacity){OpenLayers.Util.modifyDOMElement(div,id,px,sz);var img=div.childNodes[0];if(imgURL){img.src=imgURL;}
+OpenLayers.Util.modifyDOMElement(img,div.id+"_innerImage",null,sz,"relative",border);if(opacity){div.style.opacity=opacity;div.style.filter='alpha(opacity='+(opacity*100)+')';}
+if(OpenLayers.Util.alphaHack()){div.style.display="inline-block";if(sizing==null){sizing="scale";}
+div.style.filter="progid:DXImageTransform.Microsoft"+".AlphaImageLoader(src='"+img.src+"', "+"sizingMethod='"+sizing+"')";if(div.style.opacity){div.style.filter+=" alpha(opacity="+div.style.opacity*100+")";}
+img.style.filter="progid:DXImageTransform.Microsoft"+".Alpha(opacity=0)";}};OpenLayers.Util.createAlphaImageDiv=function(id,px,sz,imgURL,position,border,sizing,opacity,delayDisplay){var div=OpenLayers.Util.createDiv();var img=OpenLayers.Util.createImage(null,null,null,null,null,null,false);div.appendChild(img);if(delayDisplay){img.style.display="none";OpenLayers.Event.observe(img,"load",OpenLayers.Util.onImageLoad.bindAsEventListener(div));OpenLayers.Event.observe(img,"error",OpenLayers.Util.onImageLoadError.bindAsEventListener(div));}
+OpenLayers.Util.modifyAlphaImageDiv(div,id,px,sz,imgURL,position,border,sizing,opacity);return div;};OpenLayers.Util.upperCaseObject=function(object){var uObject=new Object();for(var key in object){uObject[key.toUpperCase()]=object[key];}
+return uObject;};OpenLayers.Util.applyDefaults=function(to,from){for(var key in from){if(to[key]==null){to[key]=from[key];}}};OpenLayers.Util.getParameterString=function(params){paramsArray=new Array();for(var key in params){var value=params[key];if((value!=null)&&(typeof value!='function')){paramsArray.push(key+"="+value);}}
+return paramsArray.join("&");};OpenLayers.ImgPath='';OpenLayers.Util.getImagesLocation=function(){return OpenLayers.ImgPath||(OpenLayers._getScriptLocation()+"img/");};OpenLayers.Util.Try=function(){var returnValue;for(var i=0;i<arguments.length;i++){var lambda=arguments[i];try{returnValue=lambda();break;}catch(e){}}
+return returnValue;}
+OpenLayers.Util.getNodes=function(p,tagName){var nodes=OpenLayers.Util.Try(function(){return OpenLayers.Util._getNodes(p.documentElement.childNodes,tagName);},function(){return OpenLayers.Util._getNodes(p.childNodes,tagName);});return nodes;};OpenLayers.Util._getNodes=function(nodes,tagName){var retArray=new Array();for(var i=0;i<nodes.length;i++){if(nodes[i].nodeName==tagName){retArray.push(nodes[i]);}}
+return retArray;};OpenLayers.Util.getTagText=function(parent,item,index){var result=OpenLayers.Util.getNodes(parent,item);if(result&&(result.length>0))
+{if(!index){index=0;}
+if(result[index].childNodes.length>1){return result.childNodes[1].nodeValue;}
+else if(result[index].childNodes.length==1){return result[index].firstChild.nodeValue;}}else{return"";}};OpenLayers.Util.getXmlNodeValue=function(node){var val=null;OpenLayers.Util.Try(function(){val=node.text;if(!val)
+val=node.textContent;if(!val)
+val=node.firstChild.nodeValue;},function(){val=node.textContent;});return val;};OpenLayers.Util.mouseLeft=function(evt,div){var target=(evt.relatedTarget)?evt.relatedTarget:evt.toElement;while(target!=div&&target!=null){target=target.parentNode;}
+return(target!=div);};OpenLayers.Util.rad=function(x){return x*Math.PI/180;};OpenLayers.Util.distVincenty=function(p1,p2){var a=6378137,b=6356752.3142,f=1/298.257223563;var L=OpenLayers.Util.rad(p2.lon-p1.lon);var U1=Math.atan((1-f)*Math.tan(OpenLayers.Util.rad(p1.lat)));var U2=Math.atan((1-f)*Math.tan(OpenLayers.Util.rad(p2.lat)));var sinU1=Math.sin(U1),cosU1=Math.cos(U1);var sinU2=Math.sin(U2),cosU2=Math.cos(U2);var lambda=L,lambdaP=2*Math.PI;var iterLimit=20;while(Math.abs(lambda-lambdaP)>1e-12&&--iterLimit>0){var sinLambda=Math.sin(lambda),cosLambda=Math.cos(lambda);var sinSigma=Math.sqrt((cosU2*sinLambda)*(cosU2*sinLambda)+
+(cosU1*sinU2-sinU1*cosU2*cosLambda)*(cosU1*sinU2-sinU1*cosU2*cosLambda));if(sinSigma==0)return 0;var cosSigma=sinU1*sinU2+cosU1*cosU2*cosLambda;var sigma=Math.atan2(sinSigma,cosSigma);var alpha=Math.asin(cosU1*cosU2*sinLambda/sinSigma);var cosSqAlpha=Math.cos(alpha)*Math.cos(alpha);var cos2SigmaM=cosSigma-2*sinU1*sinU2/cosSqAlpha;var C=f/16*cosSqAlpha*(4+f*(4-3*cosSqAlpha));lambdaP=lambda;lambda=L+(1-C)*f*Math.sin(alpha)*(sigma+C*sinSigma*(cos2SigmaM+C*cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)));}
+if(iterLimit==0)return NaN
+var uSq=cosSqAlpha*(a*a-b*b)/(b*b);var A=1+uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq)));var B=uSq/1024*(256+uSq*(-128+uSq*(74-47*uSq)));var deltaSigma=B*sinSigma*(cos2SigmaM+B/4*(cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)-
+B/6*cos2SigmaM*(-3+4*sinSigma*sinSigma)*(-3+4*cos2SigmaM*cos2SigmaM)));var s=b*A*(sigma-deltaSigma);var d=s.toFixed(3)/1000;return d;};OpenLayers.Util.getArgs=function(){var args=new Object();var query=location.search.substring(1);var pairs=query.split("&");for(var i=0;i<pairs.length;i++){var pos=pairs[i].indexOf('=');if(pos==-1)continue;var argname=pairs[i].substring(0,pos);var value=pairs[i].substring(pos+1);args[argname]=unescape(value);}
+return args;};OpenLayers.Util.createUniqueID=function(prefix){if(prefix==null){prefix="id_";}
+return prefix+Math.round(Math.random()*10000);};OpenLayers.INCHES_PER_UNIT={'inches':1.0,'ft':12.0,'mi':63360.0,'m':39.3701,'km':39370.1,'dd':4374754};OpenLayers.INCHES_PER_UNIT["in"]=OpenLayers.INCHES_PER_UNIT.inches;OpenLayers.INCHES_PER_UNIT["degrees"]=OpenLayers.INCHES_PER_UNIT.dd;OpenLayers.DOTS_PER_INCH=72;OpenLayers.Util.normalizeScale=function(scale){var normScale=(scale>1.0)?(1.0/scale):scale;return normScale;};OpenLayers.Util.getResolutionFromScale=function(scale,units){if(units==null){units="degrees";}
+var normScale=OpenLayers.Util.normalizeScale(scale);var resolution=1/(normScale*OpenLayers.INCHES_PER_UNIT[units]*OpenLayers.DOTS_PER_INCH);return resolution;};OpenLayers.Util.getScaleFromResolution=function(resolution,units){if(units==null){units="degrees";}
+var scale=resolution*OpenLayers.INCHES_PER_UNIT[units]*OpenLayers.DOTS_PER_INCH;return scale;};OpenLayers.Util.safeStopPropagation=function(evt){if(evt.stopPropagation){evt.stopPropagation();}
+evt.cancelBubble=true;};OpenLayers.Util.pagePosition=function(forElement){var valueT=0,valueL=0;var element=forElement;do{valueT+=element.offsetTop||0;valueL+=element.offsetLeft||0;if(element.offsetParent==document.body)
+if(OpenLayers.Element.getStyle(element,'position')=='absolute')break;}while(element=element.offsetParent);element=forElement;do{valueT-=element.scrollTop||0;valueL-=element.scrollLeft||0;}while(element=element.parentNode);return[valueL,valueT];};var Rico=new Object();Rico.Corner={round:function(e,options){var e=$(e);this._setOptions(options);var color=this.options.color;if(this.options.color=="fromElement")
+color=this._background(e);var bgColor=this.options.bgColor;if(this.options.bgColor=="fromParent")
+bgColor=this._background(e.offsetParent);this._roundCornersImpl(e,color,bgColor);},changeColor:function(theDiv,newColor){theDiv.style.backgroundColor=newColor;var spanElements=theDiv.parentNode.getElementsByTagName("span");for(var currIdx=0;currIdx<spanElements.length;currIdx++){spanElements[currIdx].style.backgroundColor=newColor;}},changeOpacity:function(theDiv,newOpacity){var mozillaOpacity=newOpacity;var ieOpacity='alpha(opacity='+newOpacity*100+')';theDiv.style.opacity=mozillaOpacity;theDiv.style.filter=ieOpacity;var spanElements=theDiv.parentNode.getElementsByTagName("span");for(var currIdx=0;currIdx<spanElements.length;currIdx++){spanElements[currIdx].style.opacity=mozillaOpacity;spanElements[currIdx].style.filter=ieOpacity;}},reRound:function(theDiv,options){var topRico=theDiv.parentNode.childNodes[0];var bottomRico=theDiv.parentNode.childNodes[2];theDiv.parentNode.removeChild(topRico);theDiv.parentNode.removeChild(bottomRico);this.round(theDiv.parentNode,options);},_roundCornersImpl:function(e,color,bgColor){if(this.options.border)
+this._renderBorder(e,bgColor);if(this._isTopRounded())
+this._roundTopCorners(e,color,bgColor);if(this._isBottomRounded())
+this._roundBottomCorners(e,color,bgColor);},_renderBorder:function(el,bgColor){var borderValue="1px solid "+this._borderColor(bgColor);var borderL="border-left: "+borderValue;var borderR="border-right: "+borderValue;var style="style='"+borderL+";"+borderR+"'";el.innerHTML="<div "+style+">"+el.innerHTML+"</div>"},_roundTopCorners:function(el,color,bgColor){var corner=this._createCorner(bgColor);for(var i=0;i<this.options.numSlices;i++)
+corner.appendChild(this._createCornerSlice(color,bgColor,i,"top"));el.style.paddingTop=0;el.insertBefore(corner,el.firstChild);},_roundBottomCorners:function(el,color,bgColor){var corner=this._createCorner(bgColor);for(var i=(this.options.numSlices-1);i>=0;i--)
+corner.appendChild(this._createCornerSlice(color,bgColor,i,"bottom"));el.style.paddingBottom=0;el.appendChild(corner);},_createCorner:function(bgColor){var corner=document.createElement("div");corner.style.backgroundColor=(this._isTransparent()?"transparent":bgColor);return corner;},_createCornerSlice:function(color,bgColor,n,position){var slice=document.createElement("span");var inStyle=slice.style;inStyle.backgroundColor=color;inStyle.display="block";inStyle.height="1px";inStyle.overflow="hidden";inStyle.fontSize="1px";var borderColor=this._borderColor(color,bgColor);if(this.options.border&&n==0){inStyle.borderTopStyle="solid";inStyle.borderTopWidth="1px";inStyle.borderLeftWidth="0px";inStyle.borderRightWidth="0px";inStyle.borderBottomWidth="0px";inStyle.height="0px";inStyle.borderColor=borderColor;}
+else if(borderColor){inStyle.borderColor=borderColor;inStyle.borderStyle="solid";inStyle.borderWidth="0px 1px";}
+if(!this.options.compact&&(n==(this.options.numSlices-1)))
+inStyle.height="2px";this._setMargin(slice,n,position);this._setBorder(slice,n,position);return slice;},_setOptions:function(options){this.options={corners:"all",color:"fromElement",bgColor:"fromParent",blend:true,border:false,compact:false}
+OpenLayers.Util.extend(this.options,options||{});this.options.numSlices=this.options.compact?2:4;if(this._isTransparent())
+this.options.blend=false;},_whichSideTop:function(){if(this._hasString(this.options.corners,"all","top"))
+return"";if(this.options.corners.indexOf("tl")>=0&&this.options.corners.indexOf("tr")>=0)
+return"";if(this.options.corners.indexOf("tl")>=0)
+return"left";else if(this.options.corners.indexOf("tr")>=0)
+return"right";return"";},_whichSideBottom:function(){if(this._hasString(this.options.corners,"all","bottom"))
+return"";if(this.options.corners.indexOf("bl")>=0&&this.options.corners.indexOf("br")>=0)
+return"";if(this.options.corners.indexOf("bl")>=0)
+return"left";else if(this.options.corners.indexOf("br")>=0)
+return"right";return"";},_borderColor:function(color,bgColor){if(color=="transparent")
+return bgColor;else if(this.options.border)
+return this.options.border;else if(this.options.blend)
+return this._blend(bgColor,color);else
+return"";},_setMargin:function(el,n,corners){var marginSize=this._marginSize(n);var whichSide=corners=="top"?this._whichSideTop():this._whichSideBottom();if(whichSide=="left"){el.style.marginLeft=marginSize+"px";el.style.marginRight="0px";}
+else if(whichSide=="right"){el.style.marginRight=marginSize+"px";el.style.marginLeft="0px";}
+else{el.style.marginLeft=marginSize+"px";el.style.marginRight=marginSize+"px";}},_setBorder:function(el,n,corners){var borderSize=this._borderSize(n);var whichSide=corners=="top"?this._whichSideTop():this._whichSideBottom();if(whichSide=="left"){el.style.borderLeftWidth=borderSize+"px";el.style.borderRightWidth="0px";}
+else if(whichSide=="right"){el.style.borderRightWidth=borderSize+"px";el.style.borderLeftWidth="0px";}
+else{el.style.borderLeftWidth=borderSize+"px";el.style.borderRightWidth=borderSize+"px";}
+if(this.options.border!=false)
+el.style.borderLeftWidth=borderSize+"px";el.style.borderRightWidth=borderSize+"px";},_marginSize:function(n){if(this._isTransparent())
+return 0;var marginSizes=[5,3,2,1];var blendedMarginSizes=[3,2,1,0];var compactMarginSizes=[2,1];var smBlendedMarginSizes=[1,0];if(this.options.compact&&this.options.blend)
+return smBlendedMarginSizes[n];else if(this.options.compact)
+return compactMarginSizes[n];else if(this.options.blend)
+return blendedMarginSizes[n];else
+return marginSizes[n];},_borderSize:function(n){var transparentBorderSizes=[5,3,2,1];var blendedBorderSizes=[2,1,1,1];var compactBorderSizes=[1,0];var actualBorderSizes=[0,2,0,0];if(this.options.compact&&(this.options.blend||this._isTransparent()))
+return 1;else if(this.options.compact)
+return compactBorderSizes[n];else if(this.options.blend)
+return blendedBorderSizes[n];else if(this.options.border)
+return actualBorderSizes[n];else if(this._isTransparent())
+return transparentBorderSizes[n];return 0;},_hasString:function(str){for(var i=1;i<arguments.length;i++)if(str.indexOf(arguments[i])>=0)return true;return false;},_blend:function(c1,c2){var cc1=Rico.Color.createFromHex(c1);cc1.blend(Rico.Color.createFromHex(c2));return cc1;},_background:function(el){try{return Rico.Color.createColorFromBackground(el).asHex();}catch(err){return"#ffffff";}},_isTransparent:function(){return this.options.color=="transparent";},_isTopRounded:function(){return this._hasString(this.options.corners,"all","top","tl","tr");},_isBottomRounded:function(){return this._hasString(this.options.corners,"all","bottom","bl","br");},_hasSingleTextChild:function(el){return el.childNodes.length==1&&el.childNodes[0].nodeType==3;}}
+OpenLayers.ProxyHost="";OpenLayers.nullHandler=function(request){alert("Unhandled request return "+request.statusText);};OpenLayers.loadURL=function(uri,params,caller,onComplete,onFailure){if(OpenLayers.ProxyHost&&uri.startsWith("http")){uri=OpenLayers.ProxyHost+escape(uri);}
+var success=(onComplete)?onComplete.bind(caller):OpenLayers.nullHandler;var failure=(onFailure)?onFailure.bind(caller):OpenLayers.nullHandler;new OpenLayers.Ajax.Request(uri,{method:'get',parameters:params,onComplete:success,onFailure:failure});};OpenLayers.parseXMLString=function(text){var index=text.indexOf('<');if(index>0){text=text.substring(index);}
+var ajaxResponse=OpenLayers.Util.Try(function(){var xmldom=new ActiveXObject('Microsoft.XMLDOM');xmldom.loadXML(text);return xmldom;},function(){return new DOMParser().parseFromString(text,'text/xml');},function(){var req=new XMLHttpRequest();req.open("GET","data:"+"text/xml"+";charset=utf-8,"+encodeURIComponent(text),false);if(req.overrideMimeType){req.overrideMimeType("text/xml");}
+req.send(null);return req.responseXML;});return ajaxResponse;};OpenLayers.Ajax={emptyFunction:function(){},getTransport:function(){return OpenLayers.Util.Try(function(){return new ActiveXObject('Msxml2.XMLHTTP')},function(){return new ActiveXObject('Microsoft.XMLHTTP')},function(){return new XMLHttpRequest()})||false;},activeRequestCount:0};OpenLayers.Ajax.Responders={responders:[],register:function(responderToAdd){for(var i=0;i<this.responders.length;i++)
+if(responderToAdd==this.responders[i])
+return;this.responders.push(responderToAdd);},dispatch:function(callback,request,transport,json){for(var i=0;i<this.responders.length;i++){responder=this.responders[i];if(responder[callback]&&typeof responder[callback]=='function'){try{responder[callback].apply(responder,[request,transport,json]);}catch(e){}}}}};OpenLayers.Ajax.Responders.register({onCreate:function(){OpenLayers.Ajax.activeRequestCount++;},onComplete:function(){OpenLayers.Ajax.activeRequestCount--;}});OpenLayers.Ajax.Base=function(){};OpenLayers.Ajax.Base.prototype={setOptions:function(options){this.options={method:'post',asynchronous:true,parameters:''}
+OpenLayers.Util.extend(this.options,options||{});},responseIsSuccess:function(){return this.transport.status==undefined||this.transport.status==0||(this.transport.status>=200&&this.transport.status<300);},responseIsFailure:function(){return!this.responseIsSuccess();}}
+OpenLayers.Ajax.Request=OpenLayers.Class.create();OpenLayers.Ajax.Request.Events=['Uninitialized','Loading','Loaded','Interactive','Complete'];OpenLayers.Ajax.Request.prototype=OpenLayers.Class.inherit(OpenLayers.Ajax.Base,{initialize:function(url,options){this.transport=OpenLayers.Ajax.getTransport();this.setOptions(options);this.request(url);},request:function(url){var parameters=this.options.parameters||'';if(parameters.length>0)parameters+='&_=';try{this.url=url;if(this.options.method=='get'&&parameters.length>0)
+this.url+=(this.url.match(/\?/)?'&':'?')+parameters;OpenLayers.Ajax.Responders.dispatch('onCreate',this,this.transport);this.transport.open(this.options.method,this.url,this.options.asynchronous);if(this.options.asynchronous){this.transport.onreadystatechange=this.onStateChange.bind(this);setTimeout((function(){this.respondToReadyState(1)}).bind(this),10);}
+this.setRequestHeaders();var body=this.options.postBody?this.options.postBody:parameters;this.transport.send(this.options.method=='post'?body:null);}catch(e){this.dispatchException(e);}},setRequestHeaders:function(){var requestHeaders=['X-Requested-With','XMLHttpRequest','X-Prototype-Version','OpenLayers'];if(this.options.method=='post'){requestHeaders.push('Content-type','application/x-www-form-urlencoded');if(this.transport.overrideMimeType)
+requestHeaders.push('Connection','close');}
+if(this.options.requestHeaders)
+requestHeaders.push.apply(requestHeaders,this.options.requestHeaders);for(var i=0;i<requestHeaders.length;i+=2)
+this.transport.setRequestHeader(requestHeaders[i],requestHeaders[i+1]);},onStateChange:function(){var readyState=this.transport.readyState;if(readyState!=1)
+this.respondToReadyState(this.transport.readyState);},header:function(name){try{return this.transport.getResponseHeader(name);}catch(e){}},evalJSON:function(){try{return eval(this.header('X-JSON'));}catch(e){}},evalResponse:function(){try{return eval(this.transport.responseText);}catch(e){this.dispatchException(e);}},respondToReadyState:function(readyState){var event=OpenLayers.Ajax.Request.Events[readyState];var transport=this.transport,json=this.evalJSON();if(event=='Complete'){try{(this.options['on'+this.transport.status]||this.options['on'+(this.responseIsSuccess()?'Success':'Failure')]||OpenLayers.Ajax.emptyFunction)(transport,json);}catch(e){this.dispatchException(e);}
+if((this.header('Content-type')||'').match(/^text\/javascript/i))
+this.evalResponse();}
+try{(this.options['on'+event]||OpenLayers.Ajax.emptyFunction)(transport,json);OpenLayers.Ajax.Responders.dispatch('on'+event,this,transport,json);}catch(e){this.dispatchException(e);}
+if(event=='Complete')
+this.transport.onreadystatechange=OpenLayers.Ajax.emptyFunction;},dispatchException:function(exception){(this.options.onException||OpenLayers.Ajax.emptyFunction)(this,exception);OpenLayers.Ajax.Responders.dispatch('onException',this,exception);}});OpenLayers.Ajax.getElementsByTagNameNS=function(parentnode,nsuri,nsprefix,tagname){return parentnode.getElementsByTagNameNS?parentnode.getElementsByTagNameNS(nsuri,tagname):parentnode.getElementsByTagName(nsprefix+':'+tagname);}
+OpenLayers.Control=OpenLayers.Class.create();OpenLayers.Control.prototype={id:null,map:null,div:null,position:null,mouseDragStart:null,initialize:function(options){OpenLayers.Util.extend(this,options);this.id=OpenLayers.Util.createUniqueID(this.CLASS_NAME+"_");},destroy:function(){this.map=null;},setMap:function(map){this.map=map;},draw:function(px){if(this.div==null){this.div=OpenLayers.Util.createDiv();this.div.id=this.id;this.div.className='olControl';}
+if(px!=null){this.position=px.clone();}
+this.moveTo(this.position);return this.div;},moveTo:function(px){if((px!=null)&&(this.div!=null)){this.div.style.left=px.x+"px";this.div.style.top=px.x+"px";}},CLASS_NAME:"OpenLayers.Control"};OpenLayers.Control.OverviewMap=OpenLayers.Class.create();OpenLayers.Control.OverviewMap.prototype=OpenLayers.Class.inherit(OpenLayers.Control,{id:"OverviewMap",element:null,ovmap:null,layers:[],minRatio:8,maxRatio:32,initialize:function(options){OpenLayers.Control.prototype.initialize.apply(this,[options]);},draw:function(){OpenLayers.Control.prototype.draw.apply(this,arguments);if(!(this.layers.length>0)){if(this.map.baseLayer){var layer=this.map.baseLayer.clone();this.layers=[layer];}else{this.map.events.register("changebaselayer",this,this.baseLayerDraw);return this.div;}}
+this.element=document.createElement('div');this.element.className='olControlOverviewMapElement';this.element.style.display='none';this.mapDiv=document.createElement('div');this.mapDiv.style.width='180px';this.mapDiv.style.height='90px';this.mapDiv.style.position='relative';this.mapDiv.style.overflow='hidden';this.mapDiv.id=OpenLayers.Util.createUniqueID('overviewMap');this.extentRectangle=document.createElement('div');this.extentRectangle.style.position='absolute';this.extentRectangle.style.zIndex=1000;this.extentRectangle.style.backgroundImage='url('+
+OpenLayers.Util.getImagesLocation()+'/blank.png)';this.extentRectangle.className='olControlOverviewMapExtentRectangle';this.mapDiv.appendChild(this.extentRectangle);this.element.appendChild(this.mapDiv);this.div.appendChild(this.element);this.div.className='olControlOverviewMapContainer';this.map.events.register('moveend',this,this.update);this.elementEvents=new OpenLayers.Events(this,this.element);this.elementEvents.register('mousedown',this,function(e){OpenLayers.Event.stop(e);});this.elementEvents.register('click',this,function(e){OpenLayers.Event.stop(e);});this.elementEvents.register('dblclick',this,function(e){OpenLayers.Event.stop(e);});this.rectEvents=new OpenLayers.Events(this,this.extentRectangle);this.rectEvents.register('mouseover',this,this.rectMouseOver);this.rectEvents.register('mouseout',this,this.rectMouseOut);this.rectEvents.register('mousedown',this,this.rectMouseDown);this.rectEvents.register('mousemove',this,this.rectMouseMove);this.rectEvents.register('mouseup',this,this.rectMouseUp);this.rectEvents.register('click',this,function(e){OpenLayers.Event.stop(e);});this.rectEvents.register('dblclick',this,this.rectDblClick);this.mapDivEvents=new OpenLayers.Events(this,this.mapDiv);this.mapDivEvents.register('click',this,this.mapDivClick);var imgLocation=OpenLayers.Util.getImagesLocation();var img=imgLocation+'layer-switcher-maximize.png';this.maximizeDiv=OpenLayers.Util.createAlphaImageDiv('olControlOverviewMapMaximizeButton',null,new OpenLayers.Size(18,18),img,'absolute');this.maximizeDiv.style.display='none';this.maximizeDiv.className='olControlOverviewMapMaximizeButton';OpenLayers.Event.observe(this.maximizeDiv,'click',this.maximizeControl.bindAsEventListener(this));this.div.appendChild(this.maximizeDiv);var img=imgLocation+'layer-switcher-minimize.png';this.minimizeDiv=OpenLayers.Util.createAlphaImageDiv('OpenLayers_Control_minimizeDiv',null,new OpenLayers.Size(18,18),img,'absolute');this.minimizeDiv.style.display='none';this.minimizeDiv.className='olControlOverviewMapMinimizeButton';OpenLayers.Event.observe(this.minimizeDiv,'click',this.minimizeControl.bindAsEventListener(this));this.div.appendChild(this.minimizeDiv);this.minimizeControl();return this.div;},baseLayerDraw:function(){this.draw();this.map.events.unregister("changebaselayer",this,this.baseLayerDraw);},rectMouseOver:function(evt){this.extentRectangle.style.cursor='move';},rectMouseOut:function(evt){this.extentRectangle.style.cursor='default';if(this.rectDragStart!=null){if(this.performedRectDrag){this.updateMapToRect();}
+document.onselectstart=null;this.rectDragStart=null;}},rectMouseDown:function(evt){if(!OpenLayers.Event.isLeftClick(evt))return;this.rectDragStart=evt.xy.clone();this.performedRectDrag=false;OpenLayers.Event.stop(evt);},rectMouseMove:function(evt){if(this.rectDragStart!=null){var deltaX=this.rectDragStart.x-evt.xy.x;var deltaY=this.rectDragStart.y-evt.xy.y;var rectPxBounds=this.getRectPxBounds();var rectTop=rectPxBounds.top;var rectLeft=rectPxBounds.left;var rectHeight=Math.abs(rectPxBounds.getHeight());var rectWidth=rectPxBounds.getWidth();var newTop=Math.max(0,(rectTop-deltaY));newTop=Math.min(newTop,this.ovmap.size.h-this.hComp-rectHeight);var newLeft=Math.max(0,(rectLeft-deltaX));newLeft=Math.min(newLeft,this.ovmap.size.w-this.wComp-rectWidth);this.setRectPxBounds(new OpenLayers.Bounds(newLeft,newTop+rectHeight,newLeft+rectWidth,newTop));this.rectDragStart=evt.xy.clone();this.performedRectDrag=true;OpenLayers.Event.stop(evt);}},rectMouseUp:function(evt){if(!OpenLayers.Event.isLeftClick(evt))return;if(this.performedRectDrag){this.updateMapToRect();OpenLayers.Event.stop(evt);}
+document.onselectstart=null;this.rectDragStart=null;},rectDblClick:function(evt){this.performedRectDrag=false;OpenLayers.Event.stop(evt);this.updateOverview();},mapDivClick:function(evt){var pxBounds=this.getRectPxBounds();var pxCenter=pxBounds.getCenterPixel();var deltaX=evt.xy.x-pxCenter.x;var deltaY=evt.xy.y-pxCenter.y;var top=pxBounds.top;var left=pxBounds.left;var height=Math.abs(pxBounds.getHeight());var width=pxBounds.getWidth();var newTop=Math.max(0,(top+deltaY));newTop=Math.min(newTop,this.ovmap.size.h-height);var newLeft=Math.max(0,(left+deltaX));newLeft=Math.min(newLeft,this.ovmap.size.w-width);this.setRectPxBounds(new OpenLayers.Bounds(newLeft,newTop+height,newLeft+width,newTop));this.updateMapToRect();OpenLayers.Event.stop(evt);},maximizeControl:function(e){this.element.style.display='';this.showToggle(false);if(e!=null){OpenLayers.Event.stop(e);}},minimizeControl:function(e){this.element.style.display='none';this.showToggle(true);if(e!=null){OpenLayers.Event.stop(e);}},showToggle:function(minimize){this.maximizeDiv.style.display=minimize?'':'none';this.minimizeDiv.style.display=minimize?'none':'';},update:function(){if(this.ovmap==null){this.createMap();}
+if(!this.isSuitableOverview()){this.updateOverview();}
+this.updateRectToMap();},isSuitableOverview:function(){var mapExtent=this.map.getExtent();var maxExtent=this.map.maxExtent;var testExtent=new OpenLayers.Bounds(Math.max(mapExtent.left,maxExtent.left),Math.max(mapExtent.bottom,maxExtent.bottom),Math.min(mapExtent.right,maxExtent.right),Math.min(mapExtent.top,maxExtent.top));var resRatio=this.ovmap.getResolution()/this.map.getResolution();return((resRatio>this.minRatio)&&(resRatio<=this.maxRatio)&&(this.ovmap.getExtent().containsBounds(testExtent)));},updateOverview:function(){var mapRes=this.map.getResolution();var targetRes=this.ovmap.getResolution();var resRatio=targetRes/mapRes;if(resRatio>this.maxRatio){targetRes=this.minRatio*mapRes;}else if(resRatio<=this.minRatio){targetRes=this.maxRatio*mapRes;}
+this.ovmap.setCenter(this.map.center,this.ovmap.getZoomForResolution(targetRes));this.updateRectToMap();},createMap:function(){this.ovmap=new OpenLayers.Map(this.mapDiv.id,{controls:[],maxResolution:'auto'});this.ovmap.addLayers(this.layers);this.ovmap.zoomToMaxExtent();this.wComp=parseInt(OpenLayers.Element.getStyle(this.extentRectangle,'border-left-width'))+
+parseInt(OpenLayers.Element.getStyle(this.extentRectangle,'border-right-width'));this.wComp=(this.wComp)?this.wComp:2;this.hComp=parseInt(OpenLayers.Element.getStyle(this.extentRectangle,'border-top-width'))+
+parseInt(OpenLayers.Element.getStyle(this.extentRectangle,'border-bottom-width'));this.hComp=(this.hComp)?this.hComp:2;},updateRectToMap:function(){if(this.map.units!='degrees'){if(this.map.projection!=this.ovmap.map.projection){alert('The overview map only works when it is in the same projection as the main map');}}
+var pxBounds=this.getRectBoundsFromMapBounds(this.map.getExtent());this.setRectPxBounds(pxBounds);},updateMapToRect:function(){var pxBounds=this.getRectPxBounds();var lonLatBounds=this.getMapBoundsFromRectBounds(pxBounds);this.map.setCenter(lonLatBounds.getCenterLonLat(),this.map.zoom);},getRectPxBounds:function(){var top=parseInt(this.extentRectangle.style.top);var left=parseInt(this.extentRectangle.style.left);var height=parseInt(this.extentRectangle.style.height);var width=parseInt(this.extentRectangle.style.width);return new OpenLayers.Bounds(left,top+height,left+width,top);},setRectPxBounds:function(pxBounds){var top=Math.max(pxBounds.top,0);var left=Math.max(pxBounds.left,0);var bottom=Math.min(pxBounds.top+Math.abs(pxBounds.getHeight()),this.ovmap.size.h-this.hComp);var right=Math.min(pxBounds.left+pxBounds.getWidth(),this.ovmap.size.w-this.wComp);this.extentRectangle.style.top=parseInt(top)+'px';this.extentRectangle.style.left=parseInt(left)+'px';this.extentRectangle.style.height=parseInt(bottom-top)+'px';this.extentRectangle.style.width=parseInt(right-left)+'px';},getRectBoundsFromMapBounds:function(lonLatBounds){var leftBottomLonLat=new OpenLayers.LonLat(lonLatBounds.left,lonLatBounds.bottom);var rightTopLonLat=new OpenLayers.LonLat(lonLatBounds.right,lonLatBounds.top);var leftBottomPx=this.getOverviewPxFromLonLat(leftBottomLonLat);var rightTopPx=this.getOverviewPxFromLonLat(rightTopLonLat);return new OpenLayers.Bounds(leftBottomPx.x,leftBottomPx.y,rightTopPx.x,rightTopPx.y);},getMapBoundsFromRectBounds:function(pxBounds){var leftBottomPx=new OpenLayers.Pixel(pxBounds.left,pxBounds.bottom);var rightTopPx=new OpenLayers.Pixel(pxBounds.right,pxBounds.top);var leftBottomLonLat=this.getLonLatFromOverviewPx(leftBottomPx);var rightTopLonLat=this.getLonLatFromOverviewPx(rightTopPx);return new OpenLayers.Bounds(leftBottomLonLat.lon,leftBottomLonLat.lat,rightTopLonLat.lon,rightTopLonLat.lat);},getLonLatFromOverviewPx:function(overviewMapPx){var size=this.ovmap.size;var res=this.ovmap.getResolution();var center=this.ovmap.getExtent().getCenterLonLat();var delta_x=overviewMapPx.x-(size.w/2);var delta_y=overviewMapPx.y-(size.h/2);return new OpenLayers.LonLat(center.lon+delta_x*res,center.lat-delta_y*res);},getOverviewPxFromLonLat:function(lonlat){var res=this.ovmap.getResolution();var extent=this.ovmap.getExtent();return new OpenLayers.Pixel(Math.round(1/res*(lonlat.lon-extent.left)),Math.round(1/res*(extent.top-lonlat.lat)));},CLASS_NAME:'OpenLayers.Control.OverviewMap'});OpenLayers.Event={KEY_BACKSPACE:8,KEY_TAB:9,KEY_RETURN:13,KEY_ESC:27,KEY_LEFT:37,KEY_UP:38,KEY_RIGHT:39,KEY_DOWN:40,KEY_DELETE:46,element:function(event){return event.target||event.srcElement;},isLeftClick:function(event){return(((event.which)&&(event.which==1))||((event.button)&&(event.button==1)));},pointerX:function(event){return event.pageX||(event.clientX+
+(document.documentElement.scrollLeft||document.body.scrollLeft));},pointerY:function(event){return event.pageY||(event.clientY+
+(document.documentElement.scrollTop||document.body.scrollTop));},stop:function(event){if(event.preventDefault){event.preventDefault();event.stopPropagation();}else{event.returnValue=false;event.cancelBubble=true;}},findElement:function(event,tagName){var element=OpenLayers.Event.element(event);while(element.parentNode&&(!element.tagName||(element.tagName.toUpperCase()!=tagName.toUpperCase())))
+element=element.parentNode;return element;},observers:false,_observeAndCache:function(element,name,observer,useCapture){if(!this.observers)this.observers=[];if(element.addEventListener){this.observers.push([element,name,observer,useCapture]);element.addEventListener(name,observer,useCapture);}else if(element.attachEvent){this.observers.push([element,name,observer,useCapture]);element.attachEvent('on'+name,observer);}},unloadCache:function(){if(!OpenLayers.Event.observers)return;for(var i=0;i<OpenLayers.Event.observers.length;i++){OpenLayers.Event.stopObserving.apply(this,OpenLayers.Event.observers[i]);OpenLayers.Event.observers[i][0]=null;}
+OpenLayers.Event.observers=false;},observe:function(elementParam,name,observer,useCapture){var element=$(elementParam);useCapture=useCapture||false;if(name=='keypress'&&(navigator.appVersion.match(/Konqueror|Safari|KHTML/)||element.attachEvent))
+name='keydown';this._observeAndCache(element,name,observer,useCapture);},stopObserving:function(elementParam,name,observer,useCapture){var element=$(elementParam);useCapture=useCapture||false;if(name=='keypress'&&(navigator.appVersion.match(/Konqueror|Safari|KHTML/)||element.detachEvent))
+name='keydown';if(element.removeEventListener){element.removeEventListener(name,observer,useCapture);}else if(element.detachEvent){element.detachEvent('on'+name,observer);}}};OpenLayers.Event.observe(window,'unload',OpenLayers.Event.unloadCache,false);if(window.Event){OpenLayers.Util.extend(window.Event,OpenLayers.Event);}else{var Event=OpenLayers.Event;}
+OpenLayers.Events=OpenLayers.Class.create();OpenLayers.Events.prototype={BROWSER_EVENTS:["mouseover","mouseout","mousedown","mouseup","mousemove","click","dblclick","resize","focus","blur"],listeners:null,object:null,element:null,eventTypes:null,initialize:function(object,element,eventTypes,fallThrough){this.object=object;this.element=element;this.eventTypes=eventTypes;this.fallThrough=fallThrough;this.listeners=new Object();if(this.eventTypes!=null)
+for(var i=0;i<this.eventTypes.length;i++)
+this.listeners[this.eventTypes[i]]=new Array();if(this.element!=null)
+this.attachToElement(element);},attachToElement:function(element){for(var i=0;i<this.BROWSER_EVENTS.length;i++){var eventType=this.BROWSER_EVENTS[i];if(this.listeners[eventType]==null)
+this.listeners[eventType]=new Array();OpenLayers.Event.observe(element,eventType,this.handleBrowserEvent.bindAsEventListener(this));}
+OpenLayers.Event.observe(element,"dragstart",OpenLayers.Event.stop);},register:function(type,obj,func){if(func!=null){if(obj==null){obj=this.object;}
+var listeners=this.listeners[type];if(listeners!=null){listeners.push({obj:obj,func:func});}}},unregister:function(type,obj,func){if(obj==null){obj=this.object;}
+var listeners=this.listeners[type];if(listeners!=null){for(var i=0;i<listeners.length;i++){if(listeners[i].obj==obj&&listeners[i].func==func){listeners.splice(i,1);break;}}}},remove:function(type){if(this.listeners[type]!=null){this.listeners[type]=new Array();}},triggerEvent:function(type,evt){if(evt==null){evt=new Object();}
+evt.object=this.object;evt.element=this.element;var listeners=this.listeners[type];if((listeners!=null)&&(listeners.length>0)){for(var i=0;i<listeners.length;i++){var callback=listeners[i];var continueChain;if(callback.obj!=null){continueChain=callback.func.call(callback.obj,evt);}else{continueChain=callback.func(evt);}
+if((continueChain!=null)&&(continueChain==false)){break;}}
+if(!this.fallThrough){OpenLayers.Util.safeStopPropagation(evt);}}},handleBrowserEvent:function(evt){evt.xy=this.getMousePosition(evt);this.triggerEvent(evt.type,evt)},getMousePosition:function(evt){if(!this.element.offsets){this.element.offsets=OpenLayers.Util.pagePosition(this.element);this.element.offsets[0]+=(document.documentElement.scrollLeft||document.body.scrollLeft);this.element.offsets[1]+=(document.documentElement.scrollTop||document.body.scrollTop);}
+return new OpenLayers.Pixel((evt.clientX+(document.documentElement.scrollLeft||document.body.scrollLeft))-this.element.offsets[0],(evt.clientY+(document.documentElement.scrollTop||document.body.scrollTop))-this.element.offsets[1]);},CLASS_NAME:"OpenLayers.Events"};OpenLayers.Icon=OpenLayers.Class.create();OpenLayers.Icon.prototype={url:null,size:null,offset:null,calculateOffset:null,imageDiv:null,px:null,initialize:function(url,size,offset,calculateOffset){this.url=url;this.size=(size)?size:new OpenLayers.Size(20,20);this.offset=offset;this.calculateOffset=(calculateOffset)?calculateOffset:function(size){return new OpenLayers.Pixel(-(size.w/2),-(size.h/2));};this.imageDiv=OpenLayers.Util.createAlphaImageDiv();},destroy:function(){this.imageDiv=null;},clone:function(){return new OpenLayers.Icon(this.url,this.size,this.offset,this.calculateOffset);},setSize:function(size){if(size!=null){this.size=size;}
+this.draw();},draw:function(px){OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv,null,null,this.size,this.url,"absolute");this.moveTo(px);return this.imageDiv;},setOpacity:function(opacity){OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv,null,null,null,null,null,null,null,opacity);},moveTo:function(px){if(px!=null){this.px=px;}
+if(this.imageDiv!=null){if(this.px==null){this.display(false);}else{if(this.calculateOffset){this.offset=this.calculateOffset(this.size);}
+var offsetPx=this.px.offset(this.offset);OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv,null,offsetPx);this.display(true);}}},display:function(display){this.imageDiv.style.display=(display)?"":"none";},CLASS_NAME:"OpenLayers.Icon"};OpenLayers.Layer=OpenLayers.Class.create();OpenLayers.Layer.prototype={id:null,name:null,div:null,map:null,isBaseLayer:false,alpha:false,displayInLayerSwitcher:true,visibility:true,inRange:false,options:null,projection:null,units:null,scales:null,resolutions:null,maxExtent:null,minExtent:null,maxResolution:null,minResolution:null,numZoomLevels:null,minScale:null,maxScale:null,displayOutsideMaxExtent:false,initialize:function(name,options){this.options=OpenLayers.Util.extend(new Object(),options);OpenLayers.Util.extend(this,this.options);this.name=name;this.id=OpenLayers.Util.createUniqueID(this.CLASS_NAME+"_");if(this.div==null){this.div=OpenLayers.Util.createDiv();this.div.style.width="100%";this.div.style.height="100%";this.div.id=this.id;}},destroy:function(){if(this.map!=null){this.map.removeLayer(this);}
+this.map=null;this.name=null;this.div=null;this.options=null;},clone:function(obj){if(obj==null){obj=new OpenLayers.Layer(this.name,this.options);}
+OpenLayers.Util.applyDefaults(obj,this);obj.map=null;return obj;},setName:function(newName){this.name=newName;if(this.map!=null)
+this.map.events.triggerEvent("changelayer");},addOptions:function(newOptions){OpenLayers.Util.extend(this.options,newOptions);OpenLayers.Util.extend(this,newOptions);},onMapResize:function(){},moveTo:function(bounds,zoomChanged,dragging){var display=this.visibility;if(!this.isBaseLayer){display=display&&this.inRange;}
+this.display(display);},setMap:function(map){this.map=map;this.maxExtent=this.maxExtent||this.map.maxExtent;this.projection=this.projection||this.map.projection;this.units=this.units||this.map.units;this.initResolutions();if(!this.isBaseLayer){this.inRange=this.calculateInRange();}},getVisibility:function(){return this.visibility;},setVisibility:function(visibility,noEvent){if(visibility!=this.visibility){this.visibility=visibility;this.display(visibility);if(this.map!=null){var extent=this.map.getExtent();if(extent!=null){this.moveTo(extent,true);}}
+if((this.map!=null)&&((noEvent==null)||(noEvent==false))){this.map.events.triggerEvent("changelayer");}}},display:function(display){if(display!=(this.div.style.display!="none")){this.div.style.display=(display)?"block":"none";}},calculateInRange:function(){var inRange=false;if(this.map){var resolution=this.map.getResolution();inRange=((resolution>=this.minResolution)&&(resolution<=this.maxResolution));}
+return inRange;},setIsBaseLayer:function(isBaseLayer){this.isBaseLayer=isBaseLayer;if(this.map!=null){this.map.events.triggerEvent("changelayer");}},initResolutions:function(){var props=new Array('projection','units','scales','resolutions','maxScale','minScale','maxResolution','minResolution','minExtent','maxExtent','numZoomLevels','maxZoomLevel');var confProps=new Object();for(var i=0;i<props.length;i++){var property=props[i];confProps[property]=this.options[property]||this.map[property];}
+if((!confProps.numZoomLevels)&&(confProps.maxZoomLevel)){confProps.numZoomLevels=confProps.maxZoomLevel+1;}
+if((confProps.scales!=null)||(confProps.resolutions!=null)){if(confProps.scales!=null){confProps.resolutions=new Array();for(var i=0;i<confProps.scales.length;i++){var scale=confProps.scales[i];confProps.resolutions[i]=OpenLayers.Util.getResolutionFromScale(scale,confProps.units);}}
+confProps.numZoomLevels=confProps.resolutions.length;}else{confProps.resolutions=new Array();if(confProps.minScale){confProps.maxResolution=OpenLayers.Util.getResolutionFromScale(confProps.minScale,confProps.units);}else if(confProps.maxResolution=="auto"){var viewSize=this.map.getSize();var wRes=confProps.maxExtent.getWidth()/viewSize.w;var hRes=confProps.maxExtent.getHeight()/viewSize.h;confProps.maxResolution=Math.max(wRes,hRes);}
+if(confProps.maxScale!=null){confProps.minResolution=OpenLayers.Util.getResolutionFromScale(confProps.maxScale);}else if((confProps.minResolution=="auto")&&(confProps.minExtent!=null)){var viewSize=this.map.getSize();var wRes=confProps.minExtent.getWidth()/viewSize.w;var hRes=confProps.minExtent.getHeight()/viewSize.h;confProps.minResolution=Math.max(wRes,hRes);}
+if(confProps.minResolution!=null){var ratio=confProps.maxResolution/confProps.minResolution;confProps.numZoomLevels=Math.floor(Math.log(ratio)/Math.log(2))+1;}
+for(var i=0;i<confProps.numZoomLevels;i++){var res=confProps.maxResolution/Math.pow(2,i)
+confProps.resolutions.push(res);}}
+confProps.resolutions.sort(function(a,b){return(b-a);});this.resolutions=confProps.resolutions;this.maxResolution=confProps.resolutions[0];var lastIndex=confProps.resolutions.length-1;this.minResolution=confProps.resolutions[lastIndex];this.scales=new Array();for(var i=0;i<confProps.resolutions.length;i++){this.scales[i]=OpenLayers.Util.getScaleFromResolution(confProps.resolutions[i],confProps.units);}
+this.minScale=this.scales[0];this.maxScale=this.scales[this.scales.length-1];this.numZoomLevels=confProps.numZoomLevels;},getResolution:function(){var zoom=this.map.getZoom();return this.resolutions[zoom];},getExtent:function(){return this.map.calculateBounds();},getZoomForExtent:function(extent){var viewSize=this.map.getSize();var idealResolution=Math.max(extent.getWidth()/viewSize.w,extent.getHeight()/viewSize.h);return this.getZoomForResolution(idealResolution);},getZoomForResolution:function(resolution){for(var i=1;i<this.resolutions.length;i++){if(this.resolutions[i]<resolution){break;}}
+return(i-1);},getLonLatFromViewPortPx:function(viewPortPx){var lonlat=null;if(viewPortPx!=null){var size=this.map.getSize();var center=this.map.getCenter();var res=this.map.getResolution();var delta_x=viewPortPx.x-(size.w/2);var delta_y=viewPortPx.y-(size.h/2);lonlat=new OpenLayers.LonLat(center.lon+delta_x*res,center.lat-delta_y*res);}
+return lonlat;},getViewPortPxFromLonLat:function(lonlat){var px=null;if(lonlat!=null){var resolution=this.map.getResolution();var extent=this.map.getExtent();px=new OpenLayers.Pixel(Math.round(1/resolution*(lonlat.lon-extent.left)),Math.round(1/resolution*(extent.top-lonlat.lat)));}
+return px;},setOpacity:function(opacity){this.opacity=opacity;for(var i=0;i<this.div.childNodes.length;++i){var element=this.div.childNodes[i];OpenLayers.Util.modifyDOMElement(element,null,null,null,null,null,null,opacity);}},setZIndex:function(zIdx){this.div.style.zIndex=zIdx;},CLASS_NAME:"OpenLayers.Layer"};OpenLayers.Layer.FixedZoomLevels=OpenLayers.Class.create();OpenLayers.Layer.FixedZoomLevels.prototype={initialize:function(){},initResolutions:function(){var props=new Array('minZoomLevel','maxZoomLevel','numZoomLevels');for(var i=0;i<props.length;i++){var property=props[i];this[property]=(this.options[property]!=null)?this.options[property]:this.map[property];}
+if((this.minZoomLevel==null)||(this.minZoomLevel<this.MIN_ZOOM_LEVEL)){this.minZoomLevel=this.MIN_ZOOM_LEVEL;}
+var limitZoomLevels=this.MAX_ZOOM_LEVEL-this.minZoomLevel+1;if(this.numZoomLevels!=null){this.numZoomLevels=Math.min(this.numZoomLevels,limitZoomLevels);}else{if(this.maxZoomLevel!=null){var zoomDiff=this.maxZoomLevel-this.minZoomLevel+1;this.numZoomLevels=Math.min(zoomDiff,limitZoomLevels);}else{this.numZoomLevels=limitZoomLevels;}}
+this.maxZoomLevel=this.minZoomLevel+this.numZoomLevels-1;if(this.RESOLUTIONS!=null){var resolutionsIndex=0;this.resolutions=[];for(var i=this.minZoomLevel;i<this.numZoomLevels;i++){this.resolutions[resolutionsIndex++]=this.RESOLUTIONS[i];}}},getResolution:function(){if(this.resolutions!=null){return OpenLayers.Layer.prototype.getResolution.apply(this,arguments);}else{var resolution=null;var viewSize=this.map.getSize();var extent=this.getExtent();if((viewSize!=null)&&(extent!=null)){resolution=Math.max(extent.getWidth()/viewSize.w,extent.getHeight()/viewSize.h);}
+return resolution;}},getExtent:function(){var extent=null;var size=this.map.getSize();var tlPx=new OpenLayers.Pixel(0,0);var tlLL=this.getLonLatFromViewPortPx(tlPx);var brPx=new OpenLayers.Pixel(size.w,size.h);var brLL=this.getLonLatFromViewPortPx(brPx);if((tlLL!=null)&&(brLL!=null)){extent=new OpenLayers.Bounds(tlLL.lon,brLL.lat,brLL.lon,tlLL.lat);}
+return extent;},getZoomForResolution:function(resolution){if(this.resolutions!=null){return OpenLayers.Layer.prototype.getZoomForResolution.apply(this,arguments);}else{var extent=OpenLayers.Layer.prototype.getExtent.apply(this,[resolution]);return this.getZoomForExtent(extent);}},getOLZoomFromMapObjectZoom:function(moZoom){var zoom=null;if(moZoom!=null){zoom=moZoom-this.minZoomLevel;}
+return zoom;},getMapObjectZoomFromOLZoom:function(olZoom){var zoom=null;if(olZoom!=null){zoom=olZoom+this.minZoomLevel;}
+return zoom;},CLASS_NAME:"FixedZoomLevels.js"};OpenLayers.Marker=OpenLayers.Class.create();OpenLayers.Marker.prototype={icon:null,lonlat:null,events:null,map:null,initialize:function(lonlat,icon){this.lonlat=lonlat;var newIcon=(icon)?icon:OpenLayers.Marker.defaultIcon();if(this.icon==null){this.icon=newIcon;}else{this.icon.url=newIcon.url;this.icon.size=newIcon.size;this.icon.offset=newIcon.offset;this.icon.calculateOffset=newIcon.calculateOffset;}
+this.events=new OpenLayers.Events(this,this.icon.imageDiv,null);},destroy:function(){this.map=null;if(this.icon!=null){this.icon.destroy();this.icon=null;}},draw:function(px){return this.icon.draw(px);},moveTo:function(px){if((px!=null)&&(this.icon!=null)){this.icon.moveTo(px);}},onScreen:function(){var onScreen=false;if(this.map){var screenBounds=this.map.getExtent();onScreen=screenBounds.containsLonLat(this.lonlat);}
+return onScreen;},inflate:function(inflate){if(this.icon){var newSize=new OpenLayers.Size(this.icon.size.w*inflate,this.icon.size.h*inflate);this.icon.setSize(newSize);}},setOpacity:function(opacity){this.icon.setOpacity(opacity);},display:function(display){this.icon.display(display);},CLASS_NAME:"OpenLayers.Marker"};OpenLayers.Marker.defaultIcon=function(){var url=OpenLayers.Util.getImagesLocation()+"marker.png";var size=new OpenLayers.Size(21,25);return new OpenLayers.Icon(url,size);};OpenLayers.Popup=OpenLayers.Class.create();OpenLayers.Popup.WIDTH=200;OpenLayers.Popup.HEIGHT=200;OpenLayers.Popup.COLOR="white";OpenLayers.Popup.OPACITY=1;OpenLayers.Popup.BORDER="0px";OpenLayers.Popup.prototype={events:null,id:"",lonlat:null,div:null,size:null,contentHTML:"",backgroundColor:"",opacity:"",border:"",contentDiv:null,padding:5,map:null,initialize:function(id,lonlat,size,contentHTML,closeBox){if(id==null){id=OpenLayers.Util.createUniqueID(this.CLASS_NAME+"_");}
+this.id=id;this.lonlat=lonlat;this.size=(size!=null)?size:new OpenLayers.Size(OpenLayers.Popup.WIDTH,OpenLayers.Popup.HEIGHT);if(contentHTML!=null){this.contentHTML=contentHTML;}
+this.backgroundColor=OpenLayers.Popup.COLOR;this.opacity=OpenLayers.Popup.OPACITY;this.border=OpenLayers.Popup.BORDER;this.div=OpenLayers.Util.createDiv(this.id,null,null,null,null,null,"hidden");this.div.className='olPopup';var id=this.div.id+"_contentDiv";this.contentDiv=OpenLayers.Util.createDiv(id,null,this.size.clone(),null,"relative",null,"hidden");this.contentDiv.className='olPopupContent';this.div.appendChild(this.contentDiv);if(closeBox==true){var closeSize=new OpenLayers.Size(17,17);var img=OpenLayers.Util.getImagesLocation()+"close.gif";var closeImg=OpenLayers.Util.createAlphaImageDiv(this.id+"_close",null,closeSize,img);closeImg.style.right=this.padding+"px";closeImg.style.top=this.padding+"px";this.div.appendChild(closeImg);var closeEvents=new OpenLayers.Events(this,closeImg);closeEvents.register("mousedown",this,this.hide);}
+this.registerEvents();},destroy:function(){if(this.map!=null){this.map.removePopup(this);}
+this.div=null;this.map=null;},draw:function(px){if(px==null){if((this.lonlat!=null)&&(this.map!=null)){px=this.map.getLayerPxFromLonLat(this.lonlat);}}
+this.setSize();this.setBackgroundColor();this.setOpacity();this.setBorder();this.setContentHTML();this.moveTo(px);return this.div;},updatePosition:function(){if((this.lonlat)&&(this.map)){var px=this.map.getLayerPxFromLonLat(this.lonlat);this.moveTo(px);}},moveTo:function(px){if((px!=null)&&(this.div!=null)){this.div.style.left=px.x+"px";this.div.style.top=px.y+"px";}},visible:function(){return OpenLayers.Element.visible(this.div);},toggle:function(){OpenLayers.Element.toggle(this.div);},show:function(){OpenLayers.Element.show(this.div);},hide:function(){OpenLayers.Element.hide(this.div);},setSize:function(size){if(size!=undefined){this.size=size;}
+if(this.div!=null){this.div.style.width=this.size.w+"px";this.div.style.height=this.size.h+"px";}},setBackgroundColor:function(color){if(color!=undefined){this.backgroundColor=color;}
+if(this.div!=null){this.div.style.backgroundColor=this.backgroundColor;}},setOpacity:function(opacity){if(opacity!=undefined){this.opacity=opacity;}
+if(this.div!=null){this.div.style.opacity=this.opacity;this.div.style.filter='alpha(opacity='+this.opacity*100+')';}},setBorder:function(border){if(border!=undefined){this.border=border;}
+if(this.div!=null){this.div.style.border=this.border;}},setContentHTML:function(contentHTML){if(contentHTML!=null){this.contentHTML=contentHTML;}
+if(this.contentDiv!=null){this.contentDiv.innerHTML=this.contentHTML;}},registerEvents:function(){this.events=new OpenLayers.Events(this,this.div,null,true);this.events.register("mousedown",this,this.onmousedown);this.events.register("mousemove",this,this.onmousemove);this.events.register("mouseup",this,this.onmouseup);this.events.register("click",this,OpenLayers.Util.safeStopPropagation);this.events.register("mouseout",this,this.onmouseout);this.events.register("dblclick",this,OpenLayers.Util.safeStopPropagation);},onmousedown:function(evt){this.mousedown=true;OpenLayers.Util.safeStopPropagation(evt);},onmousemove:function(evt){if(this.mousedown){OpenLayers.Util.safeStopPropagation(evt);}},onmouseup:function(evt){if(this.mousedown){this.mousedown=false;OpenLayers.Util.safeStopPropagation(evt);}},onmouseout:function(evt){this.mousedown=false;},CLASS_NAME:"OpenLayers.Popup"};OpenLayers.Tile=OpenLayers.Class.create();OpenLayers.Tile.prototype={id:null,layer:null,url:null,bounds:null,size:null,position:null,drawn:false,initialize:function(layer,position,bounds,url,size){this.layer=layer;this.position=position;this.bounds=bounds;this.url=url;this.size=size;this.id=OpenLayers.Util.createUniqueID("Tile_");},destroy:function(){this.layer=null;this.bounds=null;this.size=null;this.position=null;},draw:function(){this.drawn=true;},moveTo:function(bounds,position,redraw){if(redraw==null){redraw=true;}
+this.clear();this.bounds=bounds.clone();this.position=position.clone();if(redraw){this.draw();}},clear:function(){this.drawn=false;},getBoundsFromBaseLayer:function(position){var topLeft=this.layer.map.getLonLatFromLayerPx(this.position);var bottomRightPx=this.position.clone();bottomRightPx.x+=this.size.w;bottomRightPx.y+=this.size.h;var bottomRight=this.layer.map.getLonLatFromLayerPx(bottomRightPx);if(topLeft.lon>bottomRight.lon){if(topLeft.lon<0){topLeft.lon=-180-(topLeft.lon+180);}else{bottomRight.lon=180+bottomRight.lon+180;}}
+bounds=new OpenLayers.Bounds(topLeft.lon,bottomRight.lat,bottomRight.lon,topLeft.lat);return bounds;},CLASS_NAME:"OpenLayers.Tile"};Rico.Color=OpenLayers.Class.create();Rico.Color.prototype={initialize:function(red,green,blue){this.rgb={r:red,g:green,b:blue};},setRed:function(r){this.rgb.r=r;},setGreen:function(g){this.rgb.g=g;},setBlue:function(b){this.rgb.b=b;},setHue:function(h){var hsb=this.asHSB();hsb.h=h;this.rgb=Rico.Color.HSBtoRGB(hsb.h,hsb.s,hsb.b);},setSaturation:function(s){var hsb=this.asHSB();hsb.s=s;this.rgb=Rico.Color.HSBtoRGB(hsb.h,hsb.s,hsb.b);},setBrightness:function(b){var hsb=this.asHSB();hsb.b=b;this.rgb=Rico.Color.HSBtoRGB(hsb.h,hsb.s,hsb.b);},darken:function(percent){var hsb=this.asHSB();this.rgb=Rico.Color.HSBtoRGB(hsb.h,hsb.s,Math.max(hsb.b-percent,0));},brighten:function(percent){var hsb=this.asHSB();this.rgb=Rico.Color.HSBtoRGB(hsb.h,hsb.s,Math.min(hsb.b+percent,1));},blend:function(other){this.rgb.r=Math.floor((this.rgb.r+other.rgb.r)/2);this.rgb.g=Math.floor((this.rgb.g+other.rgb.g)/2);this.rgb.b=Math.floor((this.rgb.b+other.rgb.b)/2);},isBright:function(){var hsb=this.asHSB();return this.asHSB().b>0.5;},isDark:function(){return!this.isBright();},asRGB:function(){return"rgb("+this.rgb.r+","+this.rgb.g+","+this.rgb.b+")";},asHex:function(){return"#"+this.rgb.r.toColorPart()+this.rgb.g.toColorPart()+this.rgb.b.toColorPart();},asHSB:function(){return Rico.Color.RGBtoHSB(this.rgb.r,this.rgb.g,this.rgb.b);},toString:function(){return this.asHex();}};Rico.Color.createFromHex=function(hexCode){if(hexCode.length==4){var shortHexCode=hexCode;var hexCode='#';for(var i=1;i<4;i++)hexCode+=(shortHexCode.charAt(i)+
+shortHexCode.charAt(i));}
+if(hexCode.indexOf('#')==0)
+hexCode=hexCode.substring(1);var red=hexCode.substring(0,2);var green=hexCode.substring(2,4);var blue=hexCode.substring(4,6);return new Rico.Color(parseInt(red,16),parseInt(green,16),parseInt(blue,16));}
+Rico.Color.createColorFromBackground=function(elem){var actualColor=RicoUtil.getElementsComputedStyle($(elem),"backgroundColor","background-color");if(actualColor=="transparent"&&elem.parentNode)
+return Rico.Color.createColorFromBackground(elem.parentNode);if(actualColor==null)
+return new Rico.Color(255,255,255);if(actualColor.indexOf("rgb(")==0){var colors=actualColor.substring(4,actualColor.length-1);var colorArray=colors.split(",");return new Rico.Color(parseInt(colorArray[0]),parseInt(colorArray[1]),parseInt(colorArray[2]));}
+else if(actualColor.indexOf("#")==0){return Rico.Color.createFromHex(actualColor);}
+else
+return new Rico.Color(255,255,255);}
+Rico.Color.HSBtoRGB=function(hue,saturation,brightness){var red=0;var green=0;var blue=0;if(saturation==0){red=parseInt(brightness*255.0+0.5);green=red;blue=red;}
+else{var h=(hue-Math.floor(hue))*6.0;var f=h-Math.floor(h);var p=brightness*(1.0-saturation);var q=brightness*(1.0-saturation*f);var t=brightness*(1.0-(saturation*(1.0-f)));switch(parseInt(h)){case 0:red=(brightness*255.0+0.5);green=(t*255.0+0.5);blue=(p*255.0+0.5);break;case 1:red=(q*255.0+0.5);green=(brightness*255.0+0.5);blue=(p*255.0+0.5);break;case 2:red=(p*255.0+0.5);green=(brightness*255.0+0.5);blue=(t*255.0+0.5);break;case 3:red=(p*255.0+0.5);green=(q*255.0+0.5);blue=(brightness*255.0+0.5);break;case 4:red=(t*255.0+0.5);green=(p*255.0+0.5);blue=(brightness*255.0+0.5);break;case 5:red=(brightness*255.0+0.5);green=(p*255.0+0.5);blue=(q*255.0+0.5);break;}}
+return{r:parseInt(red),g:parseInt(green),b:parseInt(blue)};}
+Rico.Color.RGBtoHSB=function(r,g,b){var hue;var saturation;var brightness;var cmax=(r>g)?r:g;if(b>cmax)
+cmax=b;var cmin=(r<g)?r:g;if(b<cmin)
+cmin=b;brightness=cmax/255.0;if(cmax!=0)
+saturation=(cmax-cmin)/cmax;else
+saturation=0;if(saturation==0)
+hue=0;else{var redc=(cmax-r)/(cmax-cmin);var greenc=(cmax-g)/(cmax-cmin);var bluec=(cmax-b)/(cmax-cmin);if(r==cmax)
+hue=bluec-greenc;else if(g==cmax)
+hue=2.0+redc-bluec;else
+hue=4.0+greenc-redc;hue=hue/6.0;if(hue<0)
+hue=hue+1.0;}
+return{h:hue,s:saturation,b:brightness};}
+OpenLayers.Control.ArgParser=OpenLayers.Class.create();OpenLayers.Control.ArgParser.prototype=OpenLayers.Class.inherit(OpenLayers.Control,{center:null,zoom:null,layers:null,initialize:function(element,base){OpenLayers.Control.prototype.initialize.apply(this,arguments);},setMap:function(map){OpenLayers.Control.prototype.setMap.apply(this,arguments);for(var i=0;i<this.map.controls.length;i++){var control=this.map.controls[i];if((control!=this)&&(control.CLASS_NAME=="OpenLayers.Control.ArgParser")){break;}}
+if(i==this.map.controls.length){var args=OpenLayers.Util.getArgs();if(args.lat&&args.lon){this.center=new OpenLayers.LonLat(parseFloat(args.lon),parseFloat(args.lat));if(args.zoom){this.zoom=parseInt(args.zoom);}
+this.map.events.register('changebaselayer',this,this.setCenter);this.setCenter();}
+if(args.layers){this.layers=args.layers;this.map.events.register('addlayer',this,this.configureLayers);this.configureLayers();}}},setCenter:function(){if(this.map.baseLayer){this.map.events.unregister('changebaselayer',this,this.setCenter);this.map.setCenter(this.center,this.zoom);}},configureLayers:function(){if(this.layers.length==this.map.layers.length){this.map.events.unregister('addlayer',this,this.configureLayers);for(var i=0;i<this.layers.length;i++){var layer=this.map.layers[i];var c=this.layers.charAt(i);if(c=="B"){this.map.setBaseLayer(layer);}else if((c=="T")||(c=="F")){layer.setVisibility(c=="T");}}}},CLASS_NAME:"OpenLayers.Control.ArgParser"});OpenLayers.Control.LayerSwitcher=OpenLayers.Class.create();OpenLayers.Control.LayerSwitcher.prototype=OpenLayers.Class.inherit(OpenLayers.Control,{activeColor:"darkblue",layersDiv:null,baseLayersDiv:null,baseLayerInputs:null,dataLbl:null,dataLayersDiv:null,dataLayerInputs:null,minimizeDiv:null,maximizeDiv:null,ascending:true,initialize:function(options){OpenLayers.Control.prototype.initialize.apply(this,arguments);},setMap:function(map){OpenLayers.Control.prototype.setMap.apply(this,arguments);this.map.events.register("addlayer",this,this.redraw);this.map.events.register("changelayer",this,this.redraw);this.map.events.register("removelayer",this,this.redraw);this.map.events.register("changebaselayer",this,this.redraw);},draw:function(){OpenLayers.Control.prototype.draw.apply(this);this.loadContents();this.minimizeControl();this.redraw();return this.div;},redraw:function(){this.baseLayersDiv.innerHTML="";this.baseLayerInputs=new Array();this.dataLayersDiv.innerHTML="";this.dataLayerInputs=new Array();var containsOverlays=false;var layers=this.map.layers.slice();if(!this.ascending){layers.reverse();}
+for(var i=0;i<layers.length;i++){var layer=layers[i];var baseLayer=layer.isBaseLayer;if(baseLayer||layer.displayInLayerSwitcher){if(!baseLayer){containsOverlays=true;}
+var checked=(baseLayer)?(layer==this.map.baseLayer):layer.getVisibility();var inputElem=document.createElement("input");inputElem.id="input_"+layer.name;inputElem.name=(baseLayer)?"baseLayers":layer.name;inputElem.type=(baseLayer)?"radio":"checkbox";inputElem.value=layer.name;inputElem.checked=checked;inputElem.defaultChecked=checked;inputElem.layer=layer;inputElem.control=this;if(!baseLayer&&!layer.inRange){inputElem.disabled=true;}
+OpenLayers.Event.observe(inputElem,"mouseup",this.onInputClick.bindAsEventListener(inputElem));var labelSpan=document.createElement("span");if(!baseLayer&&!layer.inRange){labelSpan.style.color="gray";}
+labelSpan.innerHTML=layer.name;labelSpan.style.verticalAlign=(baseLayer)?"bottom":"baseline";OpenLayers.Event.observe(labelSpan,"click",this.onInputClick.bindAsEventListener(inputElem));var br=document.createElement("br");var groupArray=(baseLayer)?this.baseLayerInputs:this.dataLayerInputs;groupArray.push(inputElem);var groupDiv=(baseLayer)?this.baseLayersDiv:this.dataLayersDiv;groupDiv.appendChild(inputElem);groupDiv.appendChild(labelSpan);groupDiv.appendChild(br);}}
+this.dataLbl.style.display=(containsOverlays)?"":"none";return this.div;},onInputClick:function(e){if(!this.disabled){if(this.type=="radio"){this.checked=true;this.layer.map.setBaseLayer(this.layer,true);this.layer.map.events.triggerEvent("changebaselayer");}else{this.checked=!this.checked;this.control.updateMap();}}
+OpenLayers.Event.stop(e);},onLayerClick:function(e){this.updateMap();},updateMap:function(){for(var i=0;i<this.baseLayerInputs.length;i++){var input=this.baseLayerInputs[i];if(input.checked){this.map.setBaseLayer(input.layer,false);}}
+for(var i=0;i<this.dataLayerInputs.length;i++){var input=this.dataLayerInputs[i];input.layer.setVisibility(input.checked,true);}},maximizeControl:function(e){this.div.style.width="20em";this.div.style.height="";this.showControls(false);if(e!=null){OpenLayers.Event.stop(e);}},minimizeControl:function(e){this.div.style.width="0px";this.div.style.height="0px";this.showControls(true);if(e!=null){OpenLayers.Event.stop(e);}},showControls:function(minimize){this.maximizeDiv.style.display=minimize?"":"none";this.minimizeDiv.style.display=minimize?"none":"";this.layersDiv.style.display=minimize?"none":"";},loadContents:function(){this.div.style.position="absolute";this.div.style.top="10px";this.div.style.right="0px";this.div.style.left="";this.div.style.fontFamily="sans-serif";this.div.style.fontWeight="bold";this.div.style.marginTop="3px";this.div.style.marginLeft="3px";this.div.style.marginBottom="3px";this.div.style.fontSize="smaller";this.div.style.color="white";this.div.style.backgroundColor="transparent";OpenLayers.Event.observe(this.div,"mouseup",this.mouseUp.bindAsEventListener(this));OpenLayers.Event.observe(this.div,"click",this.ignoreEvent);OpenLayers.Event.observe(this.div,"mousedown",this.mouseDown.bindAsEventListener(this));OpenLayers.Event.observe(this.div,"dblclick",this.ignoreEvent);this.layersDiv=document.createElement("div");this.layersDiv.id="layersDiv";this.layersDiv.style.paddingTop="5px";this.layersDiv.style.paddingLeft="10px";this.layersDiv.style.paddingBottom="5px";this.layersDiv.style.paddingRight="75px";this.layersDiv.style.backgroundColor=this.activeColor;this.layersDiv.style.width="100%";this.layersDiv.style.height="100%";var baseLbl=document.createElement("div");baseLbl.innerHTML="<u>Base Layer</u>";baseLbl.style.marginTop="3px";baseLbl.style.marginLeft="3px";baseLbl.style.marginBottom="3px";this.baseLayersDiv=document.createElement("div");this.baseLayersDiv.style.paddingLeft="10px";this.dataLbl=document.createElement("div");this.dataLbl.innerHTML="<u>Overlays</u>";this.dataLbl.style.marginTop="3px";this.dataLbl.style.marginLeft="3px";this.dataLbl.style.marginBottom="3px";this.dataLayersDiv=document.createElement("div");this.dataLayersDiv.style.paddingLeft="10px";if(this.ascending){this.layersDiv.appendChild(baseLbl);this.layersDiv.appendChild(this.baseLayersDiv);this.layersDiv.appendChild(this.dataLbl);this.layersDiv.appendChild(this.dataLayersDiv);}else{this.layersDiv.appendChild(this.dataLbl);this.layersDiv.appendChild(this.dataLayersDiv);this.layersDiv.appendChild(baseLbl);this.layersDiv.appendChild(this.baseLayersDiv);}
+this.div.appendChild(this.layersDiv);Rico.Corner.round(this.div,{corners:"tl bl",bgColor:"transparent",color:this.activeColor,blend:false});Rico.Corner.changeOpacity(this.layersDiv,0.75);var imgLocation=OpenLayers.Util.getImagesLocation();var sz=new OpenLayers.Size(18,18);var img=imgLocation+'layer-switcher-maximize.png';this.maximizeDiv=OpenLayers.Util.createAlphaImageDiv("OpenLayers_Control_MaximizeDiv",null,sz,img,"absolute");this.maximizeDiv.style.top="5px";this.maximizeDiv.style.right="0px";this.maximizeDiv.style.left="";this.maximizeDiv.style.display="none";OpenLayers.Event.observe(this.maximizeDiv,"click",this.maximizeControl.bindAsEventListener(this));this.div.appendChild(this.maximizeDiv);var img=imgLocation+'layer-switcher-minimize.png';var sz=new OpenLayers.Size(18,18);this.minimizeDiv=OpenLayers.Util.createAlphaImageDiv("OpenLayers_Control_MinimizeDiv",null,sz,img,"absolute");this.minimizeDiv.style.top="5px";this.minimizeDiv.style.right="0px";this.minimizeDiv.style.left="";this.minimizeDiv.style.display="none";OpenLayers.Event.observe(this.minimizeDiv,"click",this.minimizeControl.bindAsEventListener(this));this.div.appendChild(this.minimizeDiv);},ignoreEvent:function(evt){OpenLayers.Event.stop(evt);},mouseDown:function(evt){this.mouseDown=true;this.ignoreEvent(evt);},mouseUp:function(evt){if(this.mouseDown){this.mouseDown=false;this.ignoreEvent(evt);}},CLASS_NAME:"OpenLayers.Control.LayerSwitcher"});OpenLayers.Control.MouseDefaults=OpenLayers.Class.create();OpenLayers.Control.MouseDefaults.prototype=OpenLayers.Class.inherit(OpenLayers.Control,{performedDrag:false,initialize:function(){OpenLayers.Control.prototype.initialize.apply(this,arguments);OpenLayers.Event.observe(window,"DOMMouseScroll",this.onWheelEvent.bindAsEventListener(this));OpenLayers.Event.observe(window,"mousewheel",this.onWheelEvent.bindAsEventListener(this));OpenLayers.Event.observe(document,"mousewheel",this.onWheelEvent.bindAsEventListener(this));},draw:function(){this.map.events.register("click",this,this.defaultClick);this.map.events.register("dblclick",this,this.defaultDblClick);this.map.events.register("mousedown",this,this.defaultMouseDown);this.map.events.register("mouseup",this,this.defaultMouseUp);this.map.events.register("mousemove",this,this.defaultMouseMove);this.map.events.register("mouseout",this,this.defaultMouseOut);},defaultClick:function(evt){if(!OpenLayers.Event.isLeftClick(evt))return;var notAfterDrag=!this.performedDrag;this.performedDrag=false;return notAfterDrag;},defaultDblClick:function(evt){var newCenter=this.map.getLonLatFromViewPortPx(evt.xy);this.map.setCenter(newCenter,this.map.zoom+1);OpenLayers.Event.stop(evt);return false;},defaultMouseDown:function(evt){if(!OpenLayers.Event.isLeftClick(evt))return;this.mouseDragStart=evt.xy.clone();this.performedDrag=false;if(evt.shiftKey){this.map.div.style.cursor="crosshair";this.zoomBox=OpenLayers.Util.createDiv('zoomBox',this.mouseDragStart,null,null,"absolute","2px solid red");this.zoomBox.style.backgroundColor="white";this.zoomBox.style.filter="alpha(opacity=50)";this.zoomBox.style.opacity="0.50";this.zoomBox.style.fontSize="1px";this.zoomBox.style.zIndex=this.map.Z_INDEX_BASE["Popup"]-1;this.map.viewPortDiv.appendChild(this.zoomBox);}
+document.onselectstart=function(){return false;}
+OpenLayers.Event.stop(evt);},defaultMouseMove:function(evt){if(this.mouseDragStart!=null){if(this.zoomBox){var deltaX=Math.abs(this.mouseDragStart.x-evt.xy.x);var deltaY=Math.abs(this.mouseDragStart.y-evt.xy.y);this.zoomBox.style.width=Math.max(1,deltaX)+"px";this.zoomBox.style.height=Math.max(1,deltaY)+"px";if(evt.xy.x<this.mouseDragStart.x){this.zoomBox.style.left=evt.xy.x+"px";}
+if(evt.xy.y<this.mouseDragStart.y){this.zoomBox.style.top=evt.xy.y+"px";}}else{var deltaX=this.mouseDragStart.x-evt.xy.x;var deltaY=this.mouseDragStart.y-evt.xy.y;var size=this.map.getSize();var newXY=new OpenLayers.Pixel(size.w/2+deltaX,size.h/2+deltaY);var newCenter=this.map.getLonLatFromViewPortPx(newXY);this.map.setCenter(newCenter,null,true);this.mouseDragStart=evt.xy.clone();this.map.div.style.cursor="move";}
+this.performedDrag=true;}},defaultMouseUp:function(evt){if(!OpenLayers.Event.isLeftClick(evt))return;if(this.zoomBox){this.zoomBoxEnd(evt);}else{if(this.performedDrag){this.map.setCenter(this.map.center);}}
+document.onselectstart=null;this.mouseDragStart=null;this.map.div.style.cursor="default";},defaultMouseOut:function(evt){if(this.mouseDragStart!=null&&OpenLayers.Util.mouseLeft(evt,this.map.div)){if(this.zoomBox){this.removeZoomBox();}
+this.mouseDragStart=null;}},defaultWheelUp:function(){this.map.zoomIn();},defaultWheelDown:function(){this.map.zoomOut();},zoomBoxEnd:function(evt){if(this.mouseDragStart!=null){if(Math.abs(this.mouseDragStart.x-evt.xy.x)>5||Math.abs(this.mouseDragStart.y-evt.xy.y)>5){var start=this.map.getLonLatFromViewPortPx(this.mouseDragStart);var end=this.map.getLonLatFromViewPortPx(evt.xy);var top=Math.max(start.lat,end.lat);var bottom=Math.min(start.lat,end.lat);var left=Math.min(start.lon,end.lon);var right=Math.max(start.lon,end.lon);var bounds=new OpenLayers.Bounds(left,bottom,right,top);this.map.zoomToExtent(bounds);}else{var end=this.map.getLonLatFromViewPortPx(evt.xy);this.map.setCenter(new OpenLayers.LonLat((end.lon),(end.lat)),this.map.getZoom()+1);}
+this.removeZoomBox();}},removeZoomBox:function(){this.map.viewPortDiv.removeChild(this.zoomBox);this.zoomBox=null;},onWheelEvent:function(e){var inMap=false;var elem=OpenLayers.Event.element(e);while(elem!=null){if(this.map&&elem==this.map.div){inMap=true;break;}
+elem=elem.parentNode;}
+if(inMap){var delta=0;if(!e){e=window.event;}
+if(e.wheelDelta){delta=e.wheelDelta/120;if(window.opera){delta=-delta;}}else if(e.detail){delta=-e.detail/3;}
+if(delta){if(delta<0){this.defaultWheelDown();}else{this.defaultWheelUp();}}
+OpenLayers.Event.stop(e);}},CLASS_NAME:"OpenLayers.Control.MouseDefaults"});OpenLayers.Control.MousePosition=OpenLayers.Class.create();OpenLayers.Control.MousePosition.prototype=OpenLayers.Class.inherit(OpenLayers.Control,{element:null,prefix:'',separator:', ',suffix:'',numdigits:5,granularity:10,lastXy:null,initialize:function(options){OpenLayers.Control.prototype.initialize.apply(this,arguments);},draw:function(){OpenLayers.Control.prototype.draw.apply(this,arguments);if(!this.element){this.div.left="";this.div.top="";this.div.className="olControlMousePosition";this.element=this.div;}
+this.redraw();return this.div;},redraw:function(evt){var lonLat;if(evt==null){lonLat=new OpenLayers.LonLat(0,0);}else{if(this.lastXy==null||Math.abs(evt.xy.x-this.lastXy.x)>this.granularity||Math.abs(evt.xy.y-this.lastXy.y)>this.granularity)
+{this.lastXy=evt.xy;return;}
+lonLat=this.map.getLonLatFromPixel(evt.xy);this.lastXy=evt.xy;}
+var digits=parseInt(this.numdigits);var newHtml=this.prefix+
+lonLat.lon.toFixed(digits)+
+this.separator+
+lonLat.lat.toFixed(digits)+
+this.suffix;if(newHtml!=this.element.innerHTML){this.element.innerHTML=newHtml;}},setMap:function(){OpenLayers.Control.prototype.setMap.apply(this,arguments);this.map.events.register('mousemove',this,this.redraw);},CLASS_NAME:"OpenLayers.Control.MousePosition"});OpenLayers.Control.PanZoom=OpenLayers.Class.create();OpenLayers.Control.PanZoom.X=4;OpenLayers.Control.PanZoom.Y=4;OpenLayers.Control.PanZoom.prototype=OpenLayers.Class.inherit(OpenLayers.Control,{slideFactor:50,buttons:null,position:null,initialize:function(){OpenLayers.Control.prototype.initialize.apply(this,arguments);this.position=new OpenLayers.Pixel(OpenLayers.Control.PanZoom.X,OpenLayers.Control.PanZoom.Y);},draw:function(px){OpenLayers.Control.prototype.draw.apply(this,arguments);px=this.position;this.buttons=new Array();var sz=new OpenLayers.Size(18,18);var centered=new OpenLayers.Pixel(px.x+sz.w/2,px.y);this._addButton("panup","north-mini.png",centered,sz);px.y=centered.y+sz.h;this._addButton("panleft","west-mini.png",px,sz);this._addButton("panright","east-mini.png",px.add(sz.w,0),sz);this._addButton("pandown","south-mini.png",centered.add(0,sz.h*2),sz);this._addButton("zoomin","zoom-plus-mini.png",centered.add(0,sz.h*3+5),sz);this._addButton("zoomworld","zoom-world-mini.png",centered.add(0,sz.h*4+5),sz);this._addButton("zoomout","zoom-minus-mini.png",centered.add(0,sz.h*5+5),sz);return this.div;},_addButton:function(id,img,xy,sz){var imgLocation=OpenLayers.Util.getImagesLocation()+img;var btn=OpenLayers.Util.createAlphaImageDiv("OpenLayers_Control_PanZoom_"+id,xy,sz,imgLocation,"absolute");this.div.appendChild(btn);btn.onmousedown=this.buttonDown.bindAsEventListener(btn);btn.ondblclick=this.doubleClick.bindAsEventListener(btn);btn.onclick=this.doubleClick.bindAsEventListener(btn);btn.action=id;btn.map=this.map;btn.slideFactor=this.slideFactor;this.buttons.push(btn);return btn;},doubleClick:function(evt){OpenLayers.Event.stop(evt);return false;},buttonDown:function(evt){if(!OpenLayers.Event.isLeftClick(evt))return;switch(this.action){case"panup":this.map.pan(0,-50);break;case"pandown":this.map.pan(0,50);break;case"panleft":this.map.pan(-50,0);break;case"panright":this.map.pan(50,0);break;case"zoomin":this.map.zoomIn();break;case"zoomout":this.map.zoomOut();break;case"zoomworld":this.map.zoomToMaxExtent();break;}
+OpenLayers.Event.stop(evt);},destroy:function(){OpenLayers.Control.prototype.destroy.apply(this,arguments);for(i=0;i<this.buttons.length;i++){this.buttons[i].map=null;}},CLASS_NAME:"OpenLayers.Control.PanZoom"});OpenLayers.Control.Permalink=OpenLayers.Class.create();OpenLayers.Control.Permalink.prototype=OpenLayers.Class.inherit(OpenLayers.Control,{element:null,base:'',initialize:function(element,base){OpenLayers.Control.prototype.initialize.apply(this,arguments);this.element=element;if(base){this.base=base;}},setMap:function(map){OpenLayers.Control.prototype.setMap.apply(this,arguments);for(var i=0;i<this.map.controls.length;i++){var control=this.map.controls[i];if(control.CLASS_NAME=="OpenLayers.Control.ArgParser"){break;}}
+if(i==this.map.controls.length){this.map.addControl(new OpenLayers.Control.ArgParser());}},draw:function(){OpenLayers.Control.prototype.draw.apply(this,arguments);if(!this.element){this.div.className='olControlPermalink';this.element=document.createElement("a");this.element.style.fontSize="smaller";this.element.innerHTML="Permalink";this.element.href="";this.div.appendChild(this.element);}
+this.map.events.register('moveend',this,this.updateLink);return this.div;},updateLink:function(){var center=this.map.getCenter();var zoom="zoom="+this.map.getZoom();var lat="lat="+Math.round(center.lat*100000)/100000;var lon="lon="+Math.round(center.lon*100000)/100000;var layers="layers=";for(var i=0;i<this.map.layers.length;i++){var layer=this.map.layers[i];if(layer.isBaseLayer){layers+=(layer==this.map.baseLayer)?"B":"0";}else{layers+=(layer.getVisibility())?"T":"F";}}
+var href=this.base+"?"+lat+"&"+lon+"&"+zoom+"&"+layers;this.element.href=href;},CLASS_NAME:"OpenLayers.Control.Permalink"});OpenLayers.Control.Scale=OpenLayers.Class.create();OpenLayers.Control.Scale.prototype=OpenLayers.Class.inherit(OpenLayers.Control,{element:null,initialize:function(element){OpenLayers.Control.prototype.initialize.apply(this,arguments);this.element=element;},draw:function(){OpenLayers.Control.prototype.draw.apply(this,arguments);if(!this.element){this.element=document.createElement("div");this.div.className="olControlScale";this.element.style.fontSize="smaller";this.div.appendChild(this.element);}
+this.map.events.register('moveend',this,this.updateScale);this.updateScale();return this.div;},updateScale:function(){var scale=this.map.getScale();if(!scale)return;if(scale>=9500&&scale<=950000){scale=Math.round(scale/1000)+"K";}else if(scale>=950000){scale=Math.round(scale/1000000)+"M";}else{scale=Math.round(scale/100)*100;}
+this.element.innerHTML="Scale = 1 : "+scale;},CLASS_NAME:"OpenLayers.Control.Scale"});OpenLayers.Feature=OpenLayers.Class.create();OpenLayers.Feature.prototype={events:null,layer:null,id:null,lonlat:null,data:null,marker:null,popup:null,initialize:function(layer,lonlat,data){this.layer=layer;this.lonlat=lonlat;this.data=(data!=null)?data:new Object();this.id=OpenLayers.Util.createUniqueID(this.CLASS_NAME+"_");},destroy:function(){if((this.layer!=null)&&(this.layer.map!=null)){if(this.popup!=null){this.layer.map.removePopup(this.popup);}}
+this.events=null;this.layer=null;this.id=null;this.lonlat=null;this.data=null;if(this.marker!=null){this.destroyMarker(this.marker);this.marker=null;}
+if(this.popup!=null){this.destroyPopup(this.popup);this.popup=null;}},onScreen:function(){var onScreen=false;if((this.layer!=null)&&(this.layer.map!=null)){var screenBounds=this.layer.map.getExtent();onScreen=screenBounds.containsLonLat(this.lonlat);}
+return onScreen;},createMarker:function(){var marker=null;if(this.lonlat!=null){this.marker=new OpenLayers.Marker(this.lonlat,this.data.icon);}
+return this.marker;},destroyMarker:function(){this.marker.destroy();},createPopup:function(){if(this.lonlat!=null){var id=this.id+"_popup";var anchor=(this.marker)?this.marker.icon:null;this.popup=new OpenLayers.Popup.AnchoredBubble(id,this.lonlat,this.data.popupSize,this.data.popupContentHTML,anchor);}
+return this.popup;},destroyPopup:function(){this.popup.destroy()},CLASS_NAME:"OpenLayers.Feature"};OpenLayers.Layer.Canvas=OpenLayers.Class.create();OpenLayers.Layer.Canvas.prototype=OpenLayers.Class.inherit(OpenLayers.Layer,{isBaseLayer:false,isFixed:true,canvas:null,lines:new Array(),initialize:function(name,options){OpenLayers.Layer.prototype.initialize.apply(this,arguments);},destroy:function(){canvas=null;OpenLayers.Layer.prototype.destroy.apply(this,arguments);},moveTo:function(bounds,zoomChanged,dragging){OpenLayers.Layer.prototype.moveTo.apply(this,arguments);this.redraw();},setStrokeColor:function(color){var ctx=this.canvas.getContext("2d");ctx.strokeStyle=color;},setStrokeWidth:function(width){var ctx=this.canvas.getContext("2d");ctx.lineWidth=width;},setAlpha:function(alpha){var ctx=this.canvas.getContext("2d");ctx.globalAlpha=alpha;},clearCanvas:function(){if(this.canvas!=null){this.canvas.getContext("2d").clearRect(0,0,this.map.getSize().w,this.map.getSize().h);}},drawLine:function(start,end){var ctx=this.canvas.getContext("2d");this.addLine(start,end);this.lines.push(new Array(start,end,ctx.strokeStyle,ctx.lineWidth,ctx.globalAlpha));},addLine:function(start,end){var ctx=this.canvas.getContext("2d");var startpx=this.map.getPixelFromLonLat(start);var endpx=this.map.getPixelFromLonLat(end);ctx.beginPath();ctx.moveTo(startpx.x,startpx.y);ctx.lineTo(endpx.x,endpx.y);ctx.closePath();ctx.stroke();},redraw:function(){if(!this.canvas){this.canvas=document.createElement("CANVAS");this.canvas.setAttribute("width",this.map.getSize().w);this.canvas.setAttribute("height",this.map.getSize().h);this.div.appendChild(this.canvas);}else{this.clearCanvas();}
+for(var i=0;i<this.lines.length;i++){this.setStrokeColor(this.lines[i][2]);this.setStrokeWidth(this.lines[i][3]);this.setAlpha(this.lines[i][4]);this.addLine(this.lines[i][0],this.lines[i][1]);}},CLASS_NAME:"OpenLayers.Layer.Canvas"});OpenLayers.Layer.EventPane=OpenLayers.Class.create();OpenLayers.Layer.EventPane.prototype=OpenLayers.Class.inherit(OpenLayers.Layer,{isBaseLayer:true,isFixed:true,pane:null,mapObject:null,initialize:function(name,options){OpenLayers.Layer.prototype.initialize.apply(this,arguments);if(this.pane==null){this.pane=OpenLayers.Util.createDiv(this.div.id+"_EventPane");}},destroy:function(){this.mapObject=null;OpenLayers.Layer.prototype.destroy.apply(this,arguments);},setMap:function(map){OpenLayers.Layer.prototype.setMap.apply(this,arguments);this.pane.style.zIndex=parseInt(this.div.style.zIndex)+1;this.pane.style.display=this.div.style.display;this.pane.style.width="100%";this.pane.style.height="100%";if(/MSIE/.test(navigator.userAgent)){this.pane.style.background="url("+OpenLayers.Util.getImagesLocation()+"blank.gif)";}
+if(this.isFixed){this.map.viewPortDiv.appendChild(this.pane);}else{this.map.layerContainerDiv.appendChild(this.pane);}
+this.loadMapObject();if(this.mapObject==null){this.loadWarningMessage();}},loadWarningMessage:function(){this.div.style.backgroundColor="darkblue";var viewSize=this.map.getSize();msgW=Math.min(viewSize.w,300);msgH=Math.min(viewSize.h,200);var size=new OpenLayers.Size(msgW,msgH);var centerPx=new OpenLayers.Pixel(viewSize.w/2,viewSize.h/2);var topLeft=centerPx.add(-size.w/2,-size.h/2);var div=OpenLayers.Util.createDiv(this.name+"_warning",topLeft,size,null,null,null,"auto");div.style.padding="7px";div.style.backgroundColor="yellow";div.innerHTML=this.getWarningHTML();this.div.appendChild(div);},display:function(display){OpenLayers.Layer.prototype.display.apply(this,arguments);this.pane.style.display=this.div.style.display;},setZIndex:function(zIndex){OpenLayers.Layer.prototype.setZIndex.apply(this,arguments);this.pane.style.zIndex=parseInt(this.div.style.zIndex)+1;},moveTo:function(bounds,zoomChanged,dragging){OpenLayers.Layer.prototype.moveTo.apply(this,arguments);if(this.mapObject!=null){var newCenter=this.map.getCenter();var newZoom=this.map.getZoom();if(newCenter!=null){var moOldCenter=this.getMapObjectCenter();var oldCenter=this.getOLLonLatFromMapObjectLonLat(moOldCenter);var moOldZoom=this.getMapObjectZoom();var oldZoom=this.getOLZoomFromMapObjectZoom(moOldZoom);if(!(newCenter.equals(oldCenter))||!(newZoom==oldZoom)){var center=this.getMapObjectLonLatFromOLLonLat(newCenter);var zoom=this.getMapObjectZoomFromOLZoom(newZoom);this.setMapObjectCenter(center,zoom);}}}},getLonLatFromViewPortPx:function(viewPortPx){var lonlat=null;if((this.mapObject!=null)&&(this.getMapObjectCenter()!=null)){var moPixel=this.getMapObjectPixelFromOLPixel(viewPortPx);var moLonLat=this.getMapObjectLonLatFromMapObjectPixel(moPixel)
+lonlat=this.getOLLonLatFromMapObjectLonLat(moLonLat);}
+return lonlat;},getViewPortPxFromLonLat:function(lonlat){var viewPortPx=null;if((this.mapObject!=null)&&(this.getMapObjectCenter()!=null)){var moLonLat=this.getMapObjectLonLatFromOLLonLat(lonlat);var moPixel=this.getMapObjectPixelFromMapObjectLonLat(moLonLat)
+viewPortPx=this.getOLPixelFromMapObjectPixel(moPixel);}
+return viewPortPx;},getOLLonLatFromMapObjectLonLat:function(moLonLat){var olLonLat=null;if(moLonLat!=null){var lon=this.getLongitudeFromMapObjectLonLat(moLonLat);var lat=this.getLatitudeFromMapObjectLonLat(moLonLat);olLonLat=new OpenLayers.LonLat(lon,lat);}
+return olLonLat;},getMapObjectLonLatFromOLLonLat:function(olLonLat){var moLatLng=null;if(olLonLat!=null){moLatLng=this.getMapObjectLonLatFromLonLat(olLonLat.lon,olLonLat.lat);}
+return moLatLng;},getOLPixelFromMapObjectPixel:function(moPixel){var olPixel=null;if(moPixel!=null){var x=this.getXFromMapObjectPixel(moPixel);var y=this.getYFromMapObjectPixel(moPixel);olPixel=new OpenLayers.Pixel(x,y);}
+return olPixel;},getMapObjectPixelFromOLPixel:function(olPixel){var moPixel=null;if(olPixel!=null){moPixel=this.getMapObjectPixelFromXY(olPixel.x,olPixel.y);}
+return moPixel;},CLASS_NAME:"OpenLayers.Layer.EventPane"});OpenLayers.Layer.HTTPRequest=OpenLayers.Class.create();OpenLayers.Layer.HTTPRequest.prototype=OpenLayers.Class.inherit(OpenLayers.Layer,{url:null,params:null,reproject:false,initialize:function(name,url,params,options){var newArguments=arguments;newArguments=[name,options];OpenLayers.Layer.prototype.initialize.apply(this,newArguments);this.url=url;this.params=OpenLayers.Util.extend(new Object(),params);},destroy:function(){this.url=null;this.params=null;OpenLayers.Layer.prototype.destroy.apply(this,arguments);},clone:function(obj){if(obj==null){obj=new OpenLayers.Layer.HTTPRequest(this.name,this.url,this.params,this.options);}
+obj=OpenLayers.Layer.prototype.clone.apply(this,[obj]);return obj;},setUrl:function(newUrl){this.url=newUrl;},mergeNewParams:function(newParams){this.params=OpenLayers.Util.extend(this.params,newParams);},getFullRequestString:function(newParams,altUrl){var url=(altUrl==null)?this.url:altUrl;var requestString=url;var allParams=OpenLayers.Util.extend(new Object(),this.params);var allParams=OpenLayers.Util.extend(allParams,newParams);var paramsString=OpenLayers.Util.getParameterString(allParams);if(paramsString!=""){var lastServerChar=this.url.charAt(this.url.length-1);if((lastServerChar=="&")||(lastServerChar=="?")){requestString+=paramsString;}else{if(this.url.indexOf('?')==-1){requestString+='?'+paramsString;}else{requestString+='&'+paramsString;}}}
+return requestString;},CLASS_NAME:"OpenLayers.Layer.HTTPRequest"});OpenLayers.Layer.Image=OpenLayers.Class.create();OpenLayers.Layer.Image.prototype=OpenLayers.Class.inherit(OpenLayers.Layer,{isBaseLayer:true,url:null,extent:null,size:null,tile:null,aspectRatio:null,initialize:function(name,url,extent,size,options){this.url=url;this.extent=extent;this.size=size;OpenLayers.Layer.prototype.initialize.apply(this,[name,options]);this.aspectRatio=(this.extent.getHeight()/this.size.h)/(this.extent.getWidth()/this.size.w);},destroy:function(){this.tile.destroy();this.tile=null;OpenLayers.Layer.prototype.destroy.apply(this,arguments);},clone:function(obj){if(obj==null){obj=new OpenLayers.Layer.Image(this.name,this.url,this.extent,this.size,this.options);}
+obj=OpenLayers.Layer.prototype.clone.apply(this,[obj]);return obj;},setMap:function(map){if(this.options.maxResolution==null){this.options.maxResolution=this.extent.getWidth()/this.size.w;}
+OpenLayers.Layer.prototype.setMap.apply(this,arguments);},moveTo:function(bounds,zoomChanged,dragging){OpenLayers.Layer.prototype.moveTo.apply(this,arguments);var firstRendering=(this.tile==null);if(zoomChanged||firstRendering){var tileWidth=this.extent.getWidth()/this.map.getResolution();var tileHeight=this.extent.getHeight()/(this.map.getResolution()*this.aspectRatio);var tileSize=new OpenLayers.Size(tileWidth,tileHeight);var ul=new OpenLayers.LonLat(this.extent.left,this.extent.top);var ulPx=this.map.getLayerPxFromLonLat(ul);if(firstRendering){this.tile=new OpenLayers.Tile.Image(this,ulPx,this.extent,this.url,tileSize);}else{this.tile.size=tileSize.clone();this.tile.position=ulPx.clone();}
+this.tile.draw();}},setUrl:function(newUrl){this.url=newUrl;this.draw();},getURL:function(bounds){return this.url;},CLASS_NAME:"OpenLayers.Layer.Image"});OpenLayers.Layer.Markers=OpenLayers.Class.create();OpenLayers.Layer.Markers.prototype=OpenLayers.Class.inherit(OpenLayers.Layer,{isBaseLayer:false,markers:null,initialize:function(name,options){OpenLayers.Layer.prototype.initialize.apply(this,arguments);this.markers=new Array();},destroy:function(){this.clearMarkers();markers=null;OpenLayers.Layer.prototype.destroy.apply(this,arguments);},moveTo:function(bounds,zoomChanged,dragging){OpenLayers.Layer.prototype.moveTo.apply(this,arguments);if(zoomChanged){this.redraw();}},addMarker:function(marker){this.markers.push(marker);if(this.map&&this.map.getExtent()){marker.map=this.map;this.drawMarker(marker);}},removeMarker:function(marker){OpenLayers.Util.removeItem(this.markers,marker);if((marker.icon!=null)&&(marker.icon.imageDiv!=null)&&(marker.icon.imageDiv.parentNode==this.div)){this.div.removeChild(marker.icon.imageDiv);}},clearMarkers:function(){if(this.markers!=null){while(this.markers.length>0){this.removeMarker(this.markers[0]);}}},redraw:function(){for(i=0;i<this.markers.length;i++){this.drawMarker(this.markers[i]);}},drawMarker:function(marker){var px=this.map.getLayerPxFromLonLat(marker.lonlat);if(px==null){marker.display(false);}else{var markerImg=marker.draw(px);if(!marker.drawn){this.div.appendChild(markerImg);marker.drawn=true;}}},CLASS_NAME:"OpenLayers.Layer.Markers"});OpenLayers.Map=OpenLayers.Class.create();OpenLayers.Map.TILE_WIDTH=256;OpenLayers.Map.TILE_HEIGHT=256;OpenLayers.Map.prototype={Z_INDEX_BASE:{BaseLayer:100,Overlay:325,Popup:750,Control:1000},EVENT_TYPES:["addlayer","removelayer","changelayer","movestart","move","moveend","zoomend","popupopen","popupclose","addmarker","removemarker","clearmarkers","mouseover","mouseout","mousemove","dragstart","drag","dragend","changebaselayer"],events:null,div:null,size:null,viewPortDiv:null,layerContainerOrigin:null,layerContainerDiv:null,layers:null,controls:null,popups:null,baseLayer:null,center:null,zoom:0,tileSize:null,projection:"EPSG:4326",units:'degrees',maxResolution:1.40625,minResolution:null,maxScale:null,minScale:null,maxExtent:null,minExtent:null,numZoomLevels:16,theme:null,initialize:function(div,options){this.div=div=$(div);var id=div.id+"_OpenLayers_ViewPort";this.viewPortDiv=OpenLayers.Util.createDiv(id,null,null,null,"relative",null,"hidden");this.viewPortDiv.style.width="100%";this.viewPortDiv.style.height="100%";this.viewPortDiv.className="olMapViewport";this.div.appendChild(this.viewPortDiv);id=div.id+"_OpenLayers_Container";this.layerContainerDiv=OpenLayers.Util.createDiv(id);this.layerContainerDiv.style.zIndex=this.Z_INDEX_BASE['Popup']-1;this.viewPortDiv.appendChild(this.layerContainerDiv);this.events=new OpenLayers.Events(this,div,this.EVENT_TYPES);this.updateSize();if(navigator.appName.contains("Microsoft")){this.events.register("resize",this,this.updateSize);}else{OpenLayers.Event.observe(window,'resize',this.updateSize.bindAsEventListener(this));}
+this.setOptions(options);var cssNode=document.createElement('link');cssNode.setAttribute('rel','stylesheet');cssNode.setAttribute('type','text/css');cssNode.setAttribute('href',this.theme);document.getElementsByTagName('head')[0].appendChild(cssNode);this.layers=[];if(this.controls==null){if(OpenLayers.Control!=null){this.controls=[new OpenLayers.Control.MouseDefaults(),new OpenLayers.Control.PanZoom(),new OpenLayers.Control.ArgParser()];}else{this.controls=[];}}
+for(var i=0;i<this.controls.length;i++){this.addControlToMap(this.controls[i]);}
+this.popups=new Array();OpenLayers.Event.observe(window,'unload',this.destroy.bindAsEventListener(this));},destroy:function(){if(this.layers!=null){for(var i=0;i<this.layers.length;i++){this.layers[i].destroy();}
+this.layers=null;}
+if(this.controls!=null){for(var i=0;i<this.controls.length;i++){this.controls[i].destroy();}
+this.controls=null;}},setOptions:function(options){this.tileSize=new OpenLayers.Size(OpenLayers.Map.TILE_WIDTH,OpenLayers.Map.TILE_HEIGHT);this.maxExtent=new OpenLayers.Bounds(-180,-90,180,90);this.theme=OpenLayers._getScriptLocation()+'theme/default/style.css';OpenLayers.Util.extend(this,options);},getTileSize:function(){return this.tileSize;},getLayer:function(id){var foundLayer=null;for(var i=0;i<this.layers.length;i++){var layer=this.layers[i];if(layer.id==id){foundLayer=layer;}}
+return foundLayer;},setLayerZIndex:function(layer,zIdx){layer.setZIndex(this.Z_INDEX_BASE[layer.isBaseLayer?'BaseLayer':'Overlay']
++zIdx*5);},addLayer:function(layer){layer.div.style.overflow="";this.setLayerZIndex(layer,this.layers.length);if(layer.isFixed){this.viewPortDiv.appendChild(layer.div);}else{this.layerContainerDiv.appendChild(layer.div);}
+this.layers.push(layer);layer.setMap(this);if(layer.isBaseLayer){if(this.baseLayer==null){this.setBaseLayer(layer);}else{layer.setVisibility(false);}}else{if(this.getCenter()!=null){layer.moveTo(this.getExtent(),true);}}
+this.events.triggerEvent("addlayer");},addLayers:function(layers){for(var i=0;i<layers.length;i++){this.addLayer(layers[i]);}},removeLayer:function(layer){if(layer.isFixed){this.viewPortDiv.removeChild(layer.div);}else{this.layerContainerDiv.removeChild(layer.div);}
+layer.map=null;OpenLayers.Util.removeItem(this.layers,layer);if(this.baseLayer==layer){this.baseLayer=null;for(i=0;i<this.layers.length;i++){var iLayer=this.layers[i];if(iLayer.isBaseLayer){this.setBaseLayer(iLayer);break;}}}
+this.events.triggerEvent("removelayer");},getNumLayers:function(){return this.layers.length;},getLayerIndex:function(layer){return OpenLayers.Util.indexOf(this.layers,layer);},setLayerIndex:function(layer,idx){var base=this.getLayerIndex(layer);if(idx<0)
+idx=0;else if(idx>this.layers.length)
+idx=this.layers.length;if(base!=idx){this.layers.splice(base,1);this.layers.splice(idx,0,layer);for(var i=0;i<this.layers.length;i++)
+this.setLayerZIndex(this.layers[i],i);this.events.triggerEvent("changelayer");}},raiseLayer:function(layer,delta){var idx=this.getLayerIndex(layer)+delta;this.setLayerIndex(layer,idx);},setBaseLayer:function(newBaseLayer,noEvent){var oldBaseLayer=this.baseLayer;if(newBaseLayer!=oldBaseLayer){if(OpenLayers.Util.indexOf(this.layers,newBaseLayer)!=-1){if(oldBaseLayer!=null){oldBaseLayer.setVisibility(false,noEvent);}
+this.baseLayer=newBaseLayer;this.baseLayer.setVisibility(true,noEvent);var center=this.getCenter();if(center!=null){if(oldBaseLayer==null){this.setCenter(center);}else{this.zoomToExtent(oldBaseLayer.getExtent());}}
+if((noEvent==null)||(noEvent==false)){this.events.triggerEvent("changebaselayer");}}}},addControl:function(control,px){this.controls.push(control);this.addControlToMap(control,px);},addControlToMap:function(control,px){control.setMap(this);var div=control.draw(px);if(div){div.style.zIndex=this.Z_INDEX_BASE['Control']+
+this.controls.length;this.viewPortDiv.appendChild(div);}},addPopup:function(popup,exclusive){if(exclusive){for(var i=0;i<this.popups.length;i++){this.removePopup(this.popups[i]);}}
+popup.map=this;this.popups.push(popup);var popupDiv=popup.draw();if(popupDiv){popupDiv.style.zIndex=this.Z_INDEX_BASE['Popup']+
+this.popups.length;this.layerContainerDiv.appendChild(popupDiv);}},removePopup:function(popup){OpenLayers.Util.removeItem(this.popups,popup);if(popup.div){try{this.layerContainerDiv.removeChild(popup.div);}
+catch(e){}}
+popup.map=null;},getSize:function(){var size=null;if(this.size!=null){size=this.size.clone();}
+return size;},updateSize:function(){var newSize=this.getCurrentSize();var oldSize=this.getSize();if(oldSize==null)
+this.size=oldSize=newSize;if(!newSize.equals(oldSize)){for(var i=0;i<this.layers.length;i++){this.layers[i].onMapResize();}
+this.size=newSize;this.events.element.offsets=null;if(this.baseLayer!=null){var center=new OpenLayers.Pixel(newSize.w/2,newSize.h/2);var centerLL=this.getLonLatFromViewPortPx(center);var zoom=this.getZoom();this.zoom=null;this.setCenter(this.getCenter(),zoom);}}},getCurrentSize:function(){var size=new OpenLayers.Size(this.div.clientWidth,this.div.clientHeight);if(size.w==0&&size.h==0){var dim=OpenLayers.Element.getDimensions(this.div);size.w=dim.width;size.h=dim.height;}
+if(size.w==0&&size.h==0){size.w=parseInt(this.div.style.width);size.h=parseInt(this.div.style.height);}
+return size;},calculateBounds:function(center,resolution){var extent=null;if(center==null){center=this.getCenter();}
+if(resolution==null){resolution=this.getResolution();}
+if((center!=null)&&(resolution!=null)){var size=this.getSize();var w_deg=size.w*resolution;var h_deg=size.h*resolution;extent=new OpenLayers.Bounds(center.lon-w_deg/2,center.lat-h_deg/2,center.lon+w_deg/2,center.lat+h_deg/2);}
+return extent;},getCenter:function(){return this.center;},getZoom:function(){return this.zoom;},pan:function(dx,dy){var centerPx=this.getViewPortPxFromLonLat(this.getCenter());var newCenterPx=centerPx.add(dx,dy);if(!newCenterPx.equals(centerPx)){var newCenterLonLat=this.getLonLatFromViewPortPx(newCenterPx);this.setCenter(newCenterLonLat);}},setCenter:function(lonlat,zoom,dragging){if(!this.center&&!this.isValidLonLat(lonlat)){lonlat=this.maxExtent.getCenterLonLat();}
+var zoomChanged=(this.isValidZoomLevel(zoom))&&(zoom!=this.getZoom());var centerChanged=(this.isValidLonLat(lonlat))&&(!lonlat.equals(this.center));if(zoomChanged||centerChanged||!dragging){if(!dragging){this.events.triggerEvent("movestart");}
+if(centerChanged){if((!zoomChanged)&&(this.center)){this.centerLayerContainer(lonlat);}
+this.center=lonlat.clone();}
+if((zoomChanged)||(this.layerContainerOrigin==null)){this.layerContainerOrigin=this.center.clone();this.layerContainerDiv.style.left="0px";this.layerContainerDiv.style.top="0px";}
+if(zoomChanged){this.zoom=zoom;for(var i=0;i<this.popups.length;i++){this.popups[i].updatePosition();}}
+var bounds=this.getExtent();this.baseLayer.moveTo(bounds,zoomChanged,dragging);for(var i=0;i<this.layers.length;i++){var layer=this.layers[i];if(!layer.isBaseLayer){var moveLayer;var inRange=layer.calculateInRange();if(layer.inRange!=inRange){layer.inRange=inRange;moveLayer=true;this.events.triggerEvent("changelayer");}else{moveLayer=(layer.visibility&&layer.inRange);}
+if(moveLayer){layer.moveTo(bounds,zoomChanged,dragging);}}}
+this.events.triggerEvent("move");if(zoomChanged){this.events.triggerEvent("zoomend");}}
+if(!dragging){this.events.triggerEvent("moveend");}},centerLayerContainer:function(lonlat){var originPx=this.getViewPortPxFromLonLat(this.layerContainerOrigin);var newPx=this.getViewPortPxFromLonLat(lonlat);if((originPx!=null)&&(newPx!=null)){this.layerContainerDiv.style.left=(originPx.x-newPx.x)+"px";this.layerContainerDiv.style.top=(originPx.y-newPx.y)+"px";}},isValidZoomLevel:function(zoomLevel){return((zoomLevel!=null)&&(zoomLevel>=0)&&(zoomLevel<this.getNumZoomLevels()));},isValidLonLat:function(lonlat){var valid=false;if(lonlat!=null){var maxExtent=this.getMaxExtent();valid=maxExtent.containsLonLat(lonlat);}
+return valid;},getProjection:function(){var projection=null;if(this.baseLayer!=null){projection=this.baseLayer.projection;}
+return projection;},getMaxResolution:function(){var maxResolution=null;if(this.baseLayer!=null){maxResolution=this.baseLayer.maxResolution;}
+return maxResolution;},getMaxExtent:function(){var maxExtent=null;if(this.baseLayer!=null){maxExtent=this.baseLayer.maxExtent;}
+return maxExtent;},getNumZoomLevels:function(){var numZoomLevels=null;if(this.baseLayer!=null){numZoomLevels=this.baseLayer.numZoomLevels;}
+return numZoomLevels;},getExtent:function(){var extent=null;if(this.baseLayer!=null){extent=this.baseLayer.getExtent();}
+return extent;},getResolution:function(){var resolution=null;if(this.baseLayer!=null){resolution=this.baseLayer.getResolution();}
+return resolution;},getScale:function(){var scale=null;if(this.baseLayer!=null){var res=this.getResolution();var units=this.baseLayer.units;scale=OpenLayers.Util.getScaleFromResolution(res,units);}
+return scale;},getZoomForExtent:function(bounds){var zoom=null;if(this.baseLayer!=null){zoom=this.baseLayer.getZoomForExtent(bounds);}
+return zoom;},getZoomForResolution:function(resolution){var zoom=null;if(this.baseLayer!=null){zoom=this.baseLayer.getZoomForResolution(resolution);}
+return zoom;},zoomTo:function(zoom){if(this.isValidZoomLevel(zoom)){this.setCenter(null,zoom);}},zoomIn:function(){this.zoomTo(this.getZoom()+1);},zoomOut:function(){this.zoomTo(this.getZoom()-1);},zoomToExtent:function(bounds){this.setCenter(bounds.getCenterLonLat(),this.getZoomForExtent(bounds));},zoomToMaxExtent:function(){this.zoomToExtent(this.getMaxExtent());},zoomToScale:function(scale){var res=OpenLayers.Util.getResolutionFromScale(scale,this.baseLayer.units);var size=this.getSize();var w_deg=size.w*res;var h_deg=size.h*res;var center=this.getCenter();var extent=new OpenLayers.Bounds(center.lon-w_deg/2,center.lat-h_deg/2,center.lon+w_deg/2,center.lat+h_deg/2);this.zoomToExtent(extent);},getLonLatFromViewPortPx:function(viewPortPx){var lonlat=null;if(this.baseLayer!=null){lonlat=this.baseLayer.getLonLatFromViewPortPx(viewPortPx);}
+return lonlat;},getViewPortPxFromLonLat:function(lonlat){var px=null;if(this.baseLayer!=null){px=this.baseLayer.getViewPortPxFromLonLat(lonlat);}
+return px;},getLonLatFromPixel:function(px){return this.getLonLatFromViewPortPx(px);},getPixelFromLonLat:function(lonlat){return this.getViewPortPxFromLonLat(lonlat);},getViewPortPxFromLayerPx:function(layerPx){var viewPortPx=null;if(layerPx!=null){var dX=parseInt(this.layerContainerDiv.style.left);var dY=parseInt(this.layerContainerDiv.style.top);viewPortPx=layerPx.add(dX,dY);}
+return viewPortPx;},getLayerPxFromViewPortPx:function(viewPortPx){var layerPx=null;if(viewPortPx!=null){var dX=-parseInt(this.layerContainerDiv.style.left);var dY=-parseInt(this.layerContainerDiv.style.top);layerPx=viewPortPx.add(dX,dY);if(isNaN(layerPx.x)||isNaN(layerPx.y)){layerPx=null;}}
+return layerPx;},getLonLatFromLayerPx:function(px){px=this.getViewPortPxFromLayerPx(px);return this.getLonLatFromViewPortPx(px);},getLayerPxFromLonLat:function(lonlat){var px=this.getViewPortPxFromLonLat(lonlat);return this.getLayerPxFromViewPortPx(px);},CLASS_NAME:"OpenLayers.Map"};OpenLayers.Marker.Box=OpenLayers.Class.create();OpenLayers.Marker.Box.prototype=OpenLayers.Class.inherit(OpenLayers.Marker,{bounds:null,div:null,initialize:function(bounds,borderColor,borderWidth){this.bounds=bounds;this.div=OpenLayers.Util.createDiv();this.div.style.overflow='hidden';this.events=new OpenLayers.Events(this,this.div,null);this.setBorder(borderColor,borderWidth);},setBorder:function(color,width){if(!color)color="red";if(!width)width=2;this.div.style.border=width+"px solid "+color;},draw:function(px,sz){OpenLayers.Util.modifyDOMElement(this.div,null,px,sz);return this.div;},onScreen:function(){var onScreen=false;if(this.map){var screenBounds=this.map.getExtent();onScreen=screenBounds.containsBounds(this.bounds,true,true);}
+return onScreen;},display:function(display){this.div.style.display=(display)?"":"none";},CLASS_NAME:"OpenLayers.Marker.Box"});OpenLayers.Popup.Anchored=OpenLayers.Class.create();OpenLayers.Popup.Anchored.prototype=OpenLayers.Class.inherit(OpenLayers.Popup,{relativePosition:null,anchor:null,initialize:function(id,lonlat,size,contentHTML,anchor,closeBox){var newArguments=new Array(id,lonlat,size,contentHTML,closeBox);OpenLayers.Popup.prototype.initialize.apply(this,newArguments);this.anchor=(anchor!=null)?anchor:{size:new OpenLayers.Size(0,0),offset:new OpenLayers.Pixel(0,0)};},draw:function(px){if(px==null){if((this.lonlat!=null)&&(this.map!=null)){px=this.map.getLayerPxFromLonLat(this.lonlat);}}
+this.relativePosition=this.calculateRelativePosition(px);return OpenLayers.Popup.prototype.draw.apply(this,arguments);},calculateRelativePosition:function(px){var lonlat=this.map.getLonLatFromLayerPx(px);var extent=this.map.getExtent();var quadrant=extent.determineQuadrant(lonlat);return OpenLayers.Bounds.oppositeQuadrant(quadrant);},moveTo:function(px){var newPx=this.calculateNewPx(px);var newArguments=new Array(newPx);OpenLayers.Popup.prototype.moveTo.apply(this,newArguments);},setSize:function(size){OpenLayers.Popup.prototype.setSize.apply(this,arguments);if((this.lonlat)&&(this.map)){var px=this.map.getLayerPxFromLonLat(this.lonlat);this.moveTo(px);}},calculateNewPx:function(px){var newPx=px.offset(this.anchor.offset);var top=(this.relativePosition.charAt(0)=='t');newPx.y+=(top)?-this.size.h:this.anchor.size.h;var left=(this.relativePosition.charAt(1)=='l');newPx.x+=(left)?-this.size.w:this.anchor.size.w;return newPx;},CLASS_NAME:"OpenLayers.Popup.Anchored"});OpenLayers.Tile.Image=OpenLayers.Class.create();OpenLayers.Tile.Image.prototype=OpenLayers.Class.inherit(OpenLayers.Tile,{imgDiv:null,initialize:function(layer,position,bounds,url,size){OpenLayers.Tile.prototype.initialize.apply(this,arguments);},destroy:function(){if((this.imgDiv!=null)&&(this.imgDiv.parentNode==this.layer.div)){this.layer.div.removeChild(this.imgDiv);}
+this.imgDiv=null;OpenLayers.Tile.prototype.destroy.apply(this,arguments);},draw:function(){OpenLayers.Tile.prototype.draw.apply(this,arguments);if(this.imgDiv==null){this.initImgDiv();}
+if(this.layer!=this.layer.map.baseLayer&&this.layer.reproject){this.bounds=this.getBoundsFromBaseLayer(this.position);}
+this.url=this.layer.getURL(this.bounds);this.imgDiv.style.display="none";if(this.layer.displayOutsideMaxExtent||(this.layer.maxExtent&&(this.bounds.intersectsBounds(this.layer.maxExtent,false)))){if(this.layer.alpha){OpenLayers.Util.modifyAlphaImageDiv(this.imgDiv,null,this.position,this.size,this.url);}else{this.imgDiv.src=this.url;OpenLayers.Util.modifyDOMElement(this.imgDiv,null,this.position,this.size);}}},clear:function(){OpenLayers.Tile.prototype.clear.apply(this,arguments);if(this.imgDiv){this.imgDiv.style.display="none";}},moveTo:function(bounds,position,redraw){if(this.layer!=this.layer.map.baseLayer&&this.layer.reproject){bounds=this.getBoundsFromBaseLayer(position);}
+this.url=this.layer.getURL(bounds);OpenLayers.Tile.prototype.moveTo.apply(this,arguments);},initImgDiv:function(){if(this.layer.alpha){this.imgDiv=OpenLayers.Util.createAlphaImageDiv(null,this.position,this.size,null,"absolute",null,null,null,true);}else{this.imgDiv=OpenLayers.Util.createImage(null,this.position,this.size,null,"absolute",null,null,true);}
+this.imgDiv.className='olTileImage';OpenLayers.Event.observe(this.imgDiv,"load",this.checkImgURL.bindAsEventListener(this));this.layer.div.appendChild(this.imgDiv);if(this.layer.opacity!=null){OpenLayers.Util.modifyDOMElement(this.imgDiv,null,null,null,null,null,null,this.layer.opacity);}},checkImgURL:function(){var loaded=this.layer.alpha?this.imgDiv.firstChild.src:this.imgDiv.src;if(loaded!=this.url){this.imgDiv.style.display="none";}},CLASS_NAME:"OpenLayers.Tile.Image"});OpenLayers.Tile.WFS=OpenLayers.Class.create();OpenLayers.Tile.WFS.prototype=OpenLayers.Class.inherit(OpenLayers.Tile,{features:null,urls:null,initialize:function(layer,position,bounds,urls,size){var newArguments=arguments;newArguments=[layer,position,bounds,null,size];OpenLayers.Tile.prototype.initialize.apply(this,newArguments);this.urls=urls;this.features=new Array();},destroy:function(){OpenLayers.Tile.prototype.destroy.apply(this,arguments);this.destroyAllFeatures();this.features=null;this.urls=null;},clear:function(){OpenLayers.Tile.prototype.clear.apply(this,arguments);this.destroyAllFeatures();},draw:function(){if(this.drawn){this.clear();}
+OpenLayers.Tile.prototype.draw.apply(this,arguments);if(this.layer.displayOutsideMaxExtent||(this.layer.maxExtent&&this.layer.maxExtent.intersectsBounds(this.bounds,false))){this.loadFeaturesForRegion(this.requestSuccess);}},loadFeaturesForRegion:function(success,failure){if(this.urls!=null){for(var i=0;i<this.urls.length;i++){var params={BBOX:this.bounds.toBBOX()};var url=this.urls[i]+"&"+
+OpenLayers.Util.getParameterString(params);OpenLayers.loadURL(url,null,this,success,failure);}}},requestSuccess:function(request){var doc=request.responseXML;if(!doc||request.fileType!="XML"){doc=OpenLayers.parseXMLString(request.responseText);}
+var resultFeatures=OpenLayers.Ajax.getElementsByTagNameNS(doc,"http://www.opengis.net/gml","gml","featureMember");this.addResults(resultFeatures);},addResults:function(results){for(var i=0;i<results.length;i++){var feature=new this.layer.featureClass(this.layer,results[i]);this.features.push(feature);}},destroyAllFeatures:function(){while(this.features.length>0){var feature=this.features.shift();feature.destroy();}},CLASS_NAME:"OpenLayers.Tile.WFS"});OpenLayers.Control.MouseToolbar=OpenLayers.Class.create();OpenLayers.Control.MouseToolbar.X=6;OpenLayers.Control.MouseToolbar.Y=300;OpenLayers.Control.MouseToolbar.prototype=OpenLayers.Class.inherit(OpenLayers.Control.MouseDefaults,{mode:null,buttons:null,direction:"vertical",buttonClicked:null,initialize:function(position,direction){OpenLayers.Control.prototype.initialize.apply(this,arguments);this.position=new OpenLayers.Pixel(OpenLayers.Control.MouseToolbar.X,OpenLayers.Control.MouseToolbar.Y);if(position){this.position=position;}
+if(direction){this.direction=direction;}
+this.measureDivs=[];},draw:function(){OpenLayers.Control.prototype.draw.apply(this,arguments);OpenLayers.Control.MouseDefaults.prototype.draw.apply(this,arguments);this.buttons=new Object();var sz=new OpenLayers.Size(28,28);var centered=this.position;this._addButton("zoombox","drag-rectangle-off.png","drag-rectangle-on.png",centered,sz,"Shift->Drag to zoom to area");centered=centered.add((this.direction=="vertical"?0:sz.w),(this.direction=="vertical"?sz.h:0));this._addButton("pan","panning-hand-off.png","panning-hand-on.png",centered,sz,"Drag the map to pan.");centered=centered.add((this.direction=="vertical"?0:sz.w),(this.direction=="vertical"?sz.h:0));this.switchModeTo("pan");return this.div;},_addButton:function(id,img,activeImg,xy,sz,title){var imgLocation=OpenLayers.Util.getImagesLocation()+img;var activeImgLocation=OpenLayers.Util.getImagesLocation()+activeImg;var btn=OpenLayers.Util.createAlphaImageDiv("OpenLayers_Control_MouseToolbar_"+id,xy,sz,imgLocation,"absolute");this.div.appendChild(btn);btn.imgLocation=imgLocation;btn.activeImgLocation=activeImgLocation;btn.events=new OpenLayers.Events(this,btn,null,true);btn.events.register("mousedown",this,this.buttonDown);btn.events.register("mouseup",this,this.buttonUp);btn.events.register("dblclick",this,OpenLayers.Event.stop);btn.action=id;btn.title=title;btn.alt=title;btn.map=this.map;this.buttons[id]=btn;return btn;},buttonDown:function(evt){if(!OpenLayers.Event.isLeftClick(evt))return;this.buttonClicked=evt.element.action;OpenLayers.Event.stop(evt);},buttonUp:function(evt){if(!OpenLayers.Event.isLeftClick(evt))return;if(this.buttonClicked!=null){if(this.buttonClicked==evt.element.action){this.switchModeTo(evt.element.action);}
+OpenLayers.Event.stop(evt);this.buttonClicked=null;}},defaultDblClick:function(evt){this.switchModeTo("pan");this.performedDrag=false;var newCenter=this.map.getLonLatFromViewPortPx(evt.xy);this.map.setCenter(newCenter,this.map.zoom+1);OpenLayers.Event.stop(evt);return false;},defaultMouseDown:function(evt){if(!OpenLayers.Event.isLeftClick(evt))return;this.mouseDragStart=evt.xy.clone();this.performedDrag=false;this.startViaKeyboard=false;if(evt.shiftKey&&this.mode!="zoombox"){this.switchModeTo("zoombox");this.startViaKeyboard=true;}else if(evt.altKey&&this.mode!="measure"){this.switchModeTo("measure");}else if(!this.mode){this.switchModeTo("pan");}
+switch(this.mode){case"zoombox":this.map.div.style.cursor="crosshair";this.zoomBox=OpenLayers.Util.createDiv('zoomBox',this.mouseDragStart,null,null,"absolute","2px solid red");this.zoomBox.style.backgroundColor="white";this.zoomBox.style.filter="alpha(opacity=50)";this.zoomBox.style.opacity="0.50";this.zoomBox.style.fontSize="1px";this.zoomBox.style.zIndex=this.map.Z_INDEX_BASE["Popup"]-1;this.map.viewPortDiv.appendChild(this.zoomBox);this.performedDrag=true;break;case"measure":var distance="";if(this.measureStart){measureEnd=this.map.getLonLatFromViewPortPx(this.mouseDragStart);distance=OpenLayers.Util.distVincenty(this.measureStart,measureEnd);distance=Math.round(distance*100)/100;distance=distance+"km";this.measureStartBox=this.measureBox;}
+this.measureStart=this.map.getLonLatFromViewPortPx(this.mouseDragStart);;this.measureBox=OpenLayers.Util.createDiv(null,this.mouseDragStart.add(-2-parseInt(this.map.layerContainerDiv.style.left),-2-parseInt(this.map.layerContainerDiv.style.top)),null,null,"absolute");this.measureBox.style.width="4px";this.measureBox.style.height="4px";this.measureBox.style.fontSize="1px";this.measureBox.style.backgroundColor="red";this.measureBox.style.zIndex=this.map.Z_INDEX_BASE["Popup"]-1;this.map.layerContainerDiv.appendChild(this.measureBox);if(distance){this.measureBoxDistance=OpenLayers.Util.createDiv(null,this.mouseDragStart.add(-2-parseInt(this.map.layerContainerDiv.style.left),2-parseInt(this.map.layerContainerDiv.style.top)),null,null,"absolute");this.measureBoxDistance.innerHTML=distance;this.measureBoxDistance.style.zIndex=this.map.Z_INDEX_BASE["Popup"]-1;this.map.layerContainerDiv.appendChild(this.measureBoxDistance);this.measureDivs.push(this.measureBoxDistance);}
+this.measureBox.style.zIndex=this.map.Z_INDEX_BASE["Popup"]-1;this.map.layerContainerDiv.appendChild(this.measureBox);this.measureDivs.push(this.measureBox);break;default:this.map.div.style.cursor="move";break;}
+document.onselectstart=function(){return false;}
+OpenLayers.Event.stop(evt);},switchModeTo:function(mode){if(mode!=this.mode){if(this.mode&&this.buttons[this.mode]){OpenLayers.Util.modifyAlphaImageDiv(this.buttons[this.mode],null,null,null,this.buttons[this.mode].imgLocation);}
+if(this.mode=="measure"&&mode!="measure"){for(var i=0;i<this.measureDivs.length;i++){if(this.measureDivs[i]){this.map.layerContainerDiv.removeChild(this.measureDivs[i]);}}
+this.measureDivs=[];this.measureStart=null;}
+this.mode=mode;if(this.buttons[mode]){OpenLayers.Util.modifyAlphaImageDiv(this.buttons[mode],null,null,null,this.buttons[mode].activeImgLocation);}
+switch(this.mode){case"zoombox":this.map.div.style.cursor="crosshair";break;default:this.map.div.style.cursor="default";break;}}},leaveMode:function(){this.switchModeTo("pan");},defaultMouseMove:function(evt){if(this.mouseDragStart!=null){switch(this.mode){case"zoombox":var deltaX=Math.abs(this.mouseDragStart.x-evt.xy.x);var deltaY=Math.abs(this.mouseDragStart.y-evt.xy.y);this.zoomBox.style.width=Math.max(1,deltaX)+"px";this.zoomBox.style.height=Math.max(1,deltaY)+"px";if(evt.xy.x<this.mouseDragStart.x){this.zoomBox.style.left=evt.xy.x+"px";}
+if(evt.xy.y<this.mouseDragStart.y){this.zoomBox.style.top=evt.xy.y+"px";}
+break;default:var deltaX=this.mouseDragStart.x-evt.xy.x;var deltaY=this.mouseDragStart.y-evt.xy.y;var size=this.map.getSize();var newXY=new OpenLayers.Pixel(size.w/2+deltaX,size.h/2+deltaY);var newCenter=this.map.getLonLatFromViewPortPx(newXY);this.map.setCenter(newCenter,null,true);this.mouseDragStart=evt.xy.clone();}
+this.performedDrag=true;}},defaultMouseUp:function(evt){if(!OpenLayers.Event.isLeftClick(evt))return;switch(this.mode){case"zoombox":this.zoomBoxEnd(evt);if(this.startViaKeyboard)this.leaveMode();break;case"pan":if(this.performedDrag){this.map.setCenter(this.map.center);}}
+document.onselectstart=null;this.mouseDragStart=null;this.map.div.style.cursor="default";},defaultMouseOut:function(evt){if(this.mouseDragStart!=null&&OpenLayers.Util.mouseLeft(evt,this.map.div)){if(this.zoomBox){this.removeZoomBox();if(this.startViaKeyboard)this.leaveMode();}
+this.mouseDragStart=null;this.map.div.style.cursor="default";}},defaultClick:function(evt){if(this.performedDrag){this.performedDrag=false;return false;}}});OpenLayers.Control.PanZoomBar=OpenLayers.Class.create();OpenLayers.Control.PanZoomBar.X=4;OpenLayers.Control.PanZoomBar.Y=4;OpenLayers.Control.PanZoomBar.prototype=OpenLayers.Class.inherit(OpenLayers.Control.PanZoom,{buttons:null,zoomStopWidth:18,zoomStopHeight:11,initialize:function(){OpenLayers.Control.PanZoom.prototype.initialize.apply(this,arguments);this.position=new OpenLayers.Pixel(OpenLayers.Control.PanZoomBar.X,OpenLayers.Control.PanZoomBar.Y);},setMap:function(map){OpenLayers.Control.PanZoom.prototype.setMap.apply(this,arguments);this.map.events.register("changebaselayer",this,this.redraw);},redraw:function(){if(this.div!=null){this.div.innerHTML="";}
+this.draw();},draw:function(px){OpenLayers.Control.prototype.draw.apply(this,arguments);px=this.position.clone();this.buttons=new Array();var sz=new OpenLayers.Size(18,18);var centered=new OpenLayers.Pixel(px.x+sz.w/2,px.y);this._addButton("panup","north-mini.png",centered,sz);px.y=centered.y+sz.h;this._addButton("panleft","west-mini.png",px,sz);this._addButton("panright","east-mini.png",px.add(sz.w,0),sz);this._addButton("pandown","south-mini.png",centered.add(0,sz.h*2),sz);this._addButton("zoomin","zoom-plus-mini.png",centered.add(0,sz.h*3+5),sz);centered=this._addZoomBar(centered.add(0,sz.h*4+5));this._addButton("zoomout","zoom-minus-mini.png",centered,sz);return this.div;},_addZoomBar:function(centered){var imgLocation=OpenLayers.Util.getImagesLocation();var id="OpenLayers_Control_PanZoomBar_Slider"+this.map.id;var zoomsToEnd=this.map.getNumZoomLevels()-1-this.map.getZoom();var slider=OpenLayers.Util.createAlphaImageDiv(id,centered.add(-1,zoomsToEnd*this.zoomStopHeight),new OpenLayers.Size(20,9),imgLocation+"slider.png","absolute");this.slider=slider;this.sliderEvents=new OpenLayers.Events(this,slider,null,true);this.sliderEvents.register("mousedown",this,this.zoomBarDown);this.sliderEvents.register("mousemove",this,this.zoomBarDrag);this.sliderEvents.register("mouseup",this,this.zoomBarUp);this.sliderEvents.register("dblclick",this,this.doubleClick);this.sliderEvents.register("click",this,this.doubleClick);sz=new OpenLayers.Size();sz.h=this.zoomStopHeight*this.map.getNumZoomLevels();sz.w=this.zoomStopWidth;var div=null
+if(OpenLayers.Util.alphaHack()){var id="OpenLayers_Control_PanZoomBar"+this.map.id;div=OpenLayers.Util.createAlphaImageDiv(id,centered,new OpenLayers.Size(sz.w,this.zoomStopHeight),imgLocation+"zoombar.png","absolute",null,"crop");div.style.height=sz.h;}else{div=OpenLayers.Util.createDiv('OpenLayers_Control_PanZoomBar_Zoombar'+this.map.id,centered,sz,imgLocation+"zoombar.png");}
+this.zoombarDiv=div;this.divEvents=new OpenLayers.Events(this,div,null,true);this.divEvents.register("mousedown",this,this.divClick);this.divEvents.register("mousemove",this,this.passEventToSlider);this.divEvents.register("dblclick",this,this.doubleClick);this.divEvents.register("click",this,this.doubleClick);this.div.appendChild(div);this.startTop=parseInt(div.style.top);this.div.appendChild(slider);this.map.events.register("zoomend",this,this.moveZoomBar);centered=centered.add(0,this.zoomStopHeight*this.map.getNumZoomLevels());return centered;},passEventToSlider:function(evt){this.sliderEvents.handleBrowserEvent(evt);},divClick:function(evt){if(!OpenLayers.Event.isLeftClick(evt))return;var y=evt.xy.y;var top=OpenLayers.Util.pagePosition(evt.object)[1];var levels=Math.floor((y-top)/this.zoomStopHeight);this.map.zoomTo((this.map.getNumZoomLevels()-1)-levels);OpenLayers.Event.stop(evt);},zoomBarDown:function(evt){if(!OpenLayers.Event.isLeftClick(evt))return;this.map.events.register("mousemove",this,this.passEventToSlider);this.map.events.register("mouseup",this,this.passEventToSlider);this.mouseDragStart=evt.xy.clone();this.zoomStart=evt.xy.clone();this.div.style.cursor="move";OpenLayers.Event.stop(evt);},zoomBarDrag:function(evt){if(this.mouseDragStart!=null){var deltaY=this.mouseDragStart.y-evt.xy.y
+var offsets=OpenLayers.Util.pagePosition(this.zoombarDiv);if((evt.clientY-offsets[1])>0&&(evt.clientY-offsets[1])<parseInt(this.zoombarDiv.style.height)-2){var newTop=parseInt(this.slider.style.top)-deltaY;this.slider.style.top=newTop+"px";}
+this.mouseDragStart=evt.xy.clone();OpenLayers.Event.stop(evt);}},zoomBarUp:function(evt){if(!OpenLayers.Event.isLeftClick(evt))return;if(this.zoomStart){this.div.style.cursor="default";this.map.events.unregister("mouseup",this,this.passEventToSlider);this.map.events.unregister("mousemove",this,this.passEventToSlider);var deltaY=this.zoomStart.y-evt.xy.y
+this.map.zoomTo(this.map.zoom+Math.round(deltaY/this.zoomStopHeight));this.moveZoomBar();this.mouseDragStart=null;OpenLayers.Event.stop(evt);}},moveZoomBar:function(){var newTop=((this.map.getNumZoomLevels()-1)-this.map.getZoom())*this.zoomStopHeight+this.startTop+1;this.slider.style.top=newTop+"px";},CLASS_NAME:"OpenLayers.Control.PanZoomBar"});OpenLayers.Feature.WFS=OpenLayers.Class.create();OpenLayers.Feature.WFS.prototype=OpenLayers.Class.inherit(OpenLayers.Feature,{initialize:function(layer,xmlNode){var newArguments=arguments;var data=this.processXMLNode(xmlNode);newArguments=new Array(layer,data.lonlat,data)
+OpenLayers.Feature.prototype.initialize.apply(this,newArguments);this.createMarker();this.layer.addMarker(this.marker);},destroy:function(){if(this.marker!=null){this.layer.removeMarker(this.marker);}
+OpenLayers.Feature.prototype.destroy.apply(this,arguments);},processXMLNode:function(xmlNode){var point=OpenLayers.Ajax.getElementsByTagNameNS(xmlNode,"http://www.opengis.net/gml","gml","Point");var text=OpenLayers.Util.getXmlNodeValue(OpenLayers.Ajax.getElementsByTagNameNS(point[0],"http://www.opengis.net/gml","gml","coordinates")[0]);var floats=text.split(",");return{lonlat:new OpenLayers.LonLat(parseFloat(floats[0]),parseFloat(floats[1])),id:null};},CLASS_NAME:"OpenLayers.Feature.WFS"});OpenLayers.Layer.Boxes=OpenLayers.Class.create();OpenLayers.Layer.Boxes.prototype=OpenLayers.Class.inherit(OpenLayers.Layer.Markers,{initialize:function(){OpenLayers.Layer.Markers.prototype.initialize.apply(this,arguments);},drawMarker:function(marker){var bounds=marker.bounds;var topleft=this.map.getLayerPxFromLonLat(new OpenLayers.LonLat(bounds.left,bounds.top));var botright=this.map.getLayerPxFromLonLat(new OpenLayers.LonLat(bounds.right,bounds.bottom));if(botright==null||topleft==null){marker.display(false);}else{var sz=new OpenLayers.Size(Math.max(1,botright.x-topleft.x),Math.max(1,botright.y-topleft.y));var markerDiv=marker.draw(topleft,sz);if(!marker.drawn){this.div.appendChild(markerDiv);marker.drawn=true;}}},CLASS_NAME:"OpenLayers.Layer.Boxes"});OpenLayers.Layer.GeoRSS=OpenLayers.Class.create();OpenLayers.Layer.GeoRSS.prototype=OpenLayers.Class.inherit(OpenLayers.Layer.Markers,{location:null,features:null,selectedFeature:null,initialize:function(name,location){OpenLayers.Layer.Markers.prototype.initialize.apply(this,[name]);this.location=location;this.features=new Array();OpenLayers.loadURL(location,null,this,this.parseData);},destroy:function(){this.clearFeatures();this.features=null;OpenLayers.Layer.Markers.prototype.destroy.apply(this,arguments);},parseData:function(ajaxRequest){var doc=ajaxRequest.responseXML;if(!doc||ajaxRequest.fileType!="XML"){doc=OpenLayers.parseXMLString(ajaxRequest.responseText);}
+this.name=null;try{this.name=doc.getElementsByTagNameNS('*','title')[0].firstChild.nodeValue;}
+catch(e){this.name=doc.getElementsByTagName('title')[0].firstChild.nodeValue;}
+var itemlist=null;try{itemlist=doc.getElementsByTagNameNS('*','item');}
+catch(e){itemlist=doc.getElementsByTagName('item');}
+if(itemlist.length==0){try{itemlist=doc.getElementsByTagNameNS('*','entry');}
+catch(e){itemlist=doc.getElementsByTagName('entry');}}
+for(var i=0;i<itemlist.length;i++){var data={};var point=OpenLayers.Util.getNodes(itemlist[i],'georss:point');var lat=OpenLayers.Util.getNodes(itemlist[i],'geo:lat');var lon=OpenLayers.Util.getNodes(itemlist[i],'geo:long');if(point.length>0){var location=point[0].firstChild.nodeValue.split(" ");if(location.length!=2){var location=point[0].firstChild.nodeValue.split(",");}}else if(lat.length>0&&lon.length>0){var location=[parseFloat(lat[0].firstChild.nodeValue),parseFloat(lon[0].firstChild.nodeValue)];}else{continue;}
+location=new OpenLayers.LonLat(parseFloat(location[1]),parseFloat(location[0]));var title="Untitled";try{title=OpenLayers.Util.getNodes(itemlist[i],"title")[0].firstChild.nodeValue;}
+catch(e){title="Untitled";}
+var descr_nodes=null;try{descr_nodes=itemlist[i].getElementsByTagNameNS("*","description");}
+catch(e){descr_nodes=itemlist[i].getElementsByTagName("description");}
+if(descr_nodes.length==0){try{descr_nodes=itemlist[i].getElementsByTagNameNS("*","summary");}
+catch(e){descr_nodes=itemlist[i].getElementsByTagName("summary");}}
+var description="No description.";try{description=descr_nodes[0].firstChild.nodeValue;}
+catch(e){description="No description.";}
+try{var link=OpenLayers.Util.getNodes(itemlist[i],"link")[0].firstChild.nodeValue;}catch(e){}
+data.icon=OpenLayers.Marker.defaultIcon();data.popupSize=new OpenLayers.Size(250,120);if((title!=null)&&(description!=null)){contentHTML='<div class="olLayerGeoRSSClose">[x]</div>';contentHTML+='<div class="olLayerGeoRSSTitle">';if(link)contentHTML+='<a class="link" href="'+link+'" target="_blank">';contentHTML+=title;if(link)contentHTML+='</a>';contentHTML+='</div>';contentHTML+='<div style="" class="olLayerGeoRSSDescription">';contentHTML+=description;contentHTML+='</div>';data['popupContentHTML']=contentHTML;}
+var feature=new OpenLayers.Feature(this,location,data);this.features.push(feature);var marker=feature.createMarker();marker.events.register('click',feature,this.markerClick);this.addMarker(marker);}},markerClick:function(evt){sameMarkerClicked=(this==this.layer.selectedFeature);this.layer.selectedFeature=(!sameMarkerClicked)?this:null;for(var i=0;i<this.layer.map.popups.length;i++){this.layer.map.removePopup(this.layer.map.popups[i]);}
+if(!sameMarkerClicked){var popup=this.createPopup();OpenLayers.Event.observe(popup.div,"click",function(){for(var i=0;i<this.layer.map.popups.length;i++){this.layer.map.removePopup(this.layer.map.popups[i]);}}.bindAsEventListener(this));this.layer.map.addPopup(popup);}
+OpenLayers.Event.stop(evt);},clearFeatures:function(){if(this.features!=null){while(this.features.length>0){var feature=this.features[0];OpenLayers.Util.removeItem(this.features,feature);feature.destroy();}}},CLASS_NAME:"OpenLayers.Layer.GeoRSS"});OpenLayers.Layer.Google=OpenLayers.Class.create();OpenLayers.Layer.Google.prototype=OpenLayers.Class.inherit(OpenLayers.Layer.EventPane,OpenLayers.Layer.FixedZoomLevels,{MIN_ZOOM_LEVEL:0,MAX_ZOOM_LEVEL:17,RESOLUTIONS:[1.40625,0.703125,0.3515625,0.17578125,0.087890625,0.0439453125,0.02197265625,0.010986328125,0.0054931640625,0.00274658203125,0.001373291015625,0.0006866455078125,0.00034332275390625,0.000171661376953125,0.0000858306884765625,0.00004291534423828125],type:null,initialize:function(name,options){OpenLayers.Layer.EventPane.prototype.initialize.apply(this,arguments);OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this,arguments);this.addContainerPxFunction();},loadMapObject:function(){try{this.mapObject=new GMap2(this.div);var poweredBy=this.div.lastChild;this.div.removeChild(poweredBy);this.pane.appendChild(poweredBy);poweredBy.className="olLayerGooglePoweredBy gmnoprint";poweredBy.style.left="";poweredBy.style.bottom="";var termsOfUse=this.div.lastChild;this.div.removeChild(termsOfUse);this.pane.appendChild(termsOfUse);termsOfUse.className="olLayerGoogleCopyright";termsOfUse.style.right="";termsOfUse.style.bottom="";}catch(e){}},setMap:function(map){OpenLayers.Layer.EventPane.prototype.setMap.apply(this,arguments);if(this.type!=null){this.map.events.register("moveend",this,this.setMapType);}},setMapType:function(){if(this.mapObject.getCenter()!=null){this.mapObject.setMapType(this.type);this.map.events.unregister("moveend",this,this.setMapType);}},onMapResize:function(){this.mapObject.checkResize();},getOLBoundsFromMapObjectBounds:function(moBounds){var olBounds=null;if(moBounds!=null){var sw=moBounds.getSouthWest();var ne=moBounds.getNorthEast();olBounds=new OpenLayers.Bounds(sw.lng(),sw.lat(),ne.lng(),ne.lat());}
+return olBounds;},getMapObjectBoundsFromOLBounds:function(olBounds){var moBounds=null;if(olBounds!=null){var sw=new GLatLng(olBounds.bottom,olBounds.left);var ne=new GLatLng(olBounds.top,olBounds.right);moBounds=new GLatLngBounds(sw,ne);}
+return moBounds;},addContainerPxFunction:function(){if(typeof GMap2!="undefined"&&!GMap2.fromLatLngToContainerPixel){GMap2.prototype.fromLatLngToContainerPixel=function(gLatLng){var gPoint=this.fromLatLngToDivPixel(gLatLng);var div=this.b.firstChild.firstChild;gPoint.x+=div.offsetLeft;gPoint.y+=div.offsetTop;return gPoint;};}},getWarningHTML:function(){var html="";html+="The Google Layer was unable to load correctly.<br>";html+="<br>";html+="To get rid of this message, select a new BaseLayer "
+html+="in the layer switcher in the upper-right corner.<br>";html+="<br>";html+="Most likely, this is because the Google Maps library";html+=" script was either not included, or does not contain the";html+=" correct API key for your site.<br>";html+="<br>";html+="Developers: For help getting this working correctly, ";html+="<a href='http://trac.openlayers.org/wiki/Google' "
+html+="target='_blank'>";html+="click here";html+="</a>";return html;},setMapObjectCenter:function(center,zoom){this.mapObject.setCenter(center,zoom);},getMapObjectCenter:function(){return this.mapObject.getCenter();},getMapObjectZoom:function(){return this.mapObject.getZoom();},getMapObjectLonLatFromMapObjectPixel:function(moPixel){return this.mapObject.fromContainerPixelToLatLng(moPixel);},getMapObjectPixelFromMapObjectLonLat:function(moLonLat){return this.mapObject.fromLatLngToContainerPixel(moLonLat);},getMapObjectZoomFromMapObjectBounds:function(moBounds){return this.mapObject.getBoundsZoomLevel(moBounds);},getLongitudeFromMapObjectLonLat:function(moLonLat){return moLonLat.lng();},getLatitudeFromMapObjectLonLat:function(moLonLat){return moLonLat.lat();},getMapObjectLonLatFromLonLat:function(lon,lat){return new GLatLng(lat,lon);},getXFromMapObjectPixel:function(moPixel){return moPixel.x;},getYFromMapObjectPixel:function(moPixel){return moPixel.y;},getMapObjectPixelFromXY:function(x,y){return new GPoint(x,y);},CLASS_NAME:"OpenLayers.Layer.Google"});OpenLayers.Layer.Grid=OpenLayers.Class.create();OpenLayers.Layer.Grid.prototype=OpenLayers.Class.inherit(OpenLayers.Layer.HTTPRequest,{tileSize:null,grid:null,buffer:2,initialize:function(name,url,params,options){OpenLayers.Layer.HTTPRequest.prototype.initialize.apply(this,arguments);this.grid=new Array();},destroy:function(){this.clearGrid();this.grid=null;this.tileSize=null;OpenLayers.Layer.HTTPRequest.prototype.destroy.apply(this,arguments);},clone:function(obj){if(obj==null){obj=new OpenLayers.Layer.Grid(this.name,this.url,this.params,this.options);}
+obj=OpenLayers.Layer.HTTPRequest.prototype.clone.apply(this,[obj]);if(this.tileSize!=null){obj.tileSize=this.tileSize.clone();}
+obj.grid=new Array();return obj;},setMap:function(map){OpenLayers.Layer.HTTPRequest.prototype.setMap.apply(this,arguments);if(this.tileSize==null){this.tileSize=this.map.getTileSize();}},moveTo:function(bounds,zoomChanged,dragging){OpenLayers.Layer.HTTPRequest.prototype.moveTo.apply(this,arguments);if(bounds==null){bounds=this.map.getExtent();}
+if(bounds!=null){if(!this.grid.length||zoomChanged||!this.getGridBounds().containsBounds(bounds,true)){this._initTiles();}else{while(true){var tlLayer=this.grid[0][0].position;var tlViewPort=this.map.getViewPortPxFromLayerPx(tlLayer);if(tlViewPort.x>-this.tileSize.w*(this.buffer-1)){this.shiftColumn(true);}else if(tlViewPort.x<-this.tileSize.w*this.buffer){this.shiftColumn(false);}else if(tlViewPort.y>-this.tileSize.h*(this.buffer-1)){this.shiftRow(true);}else if(tlViewPort.y<-this.tileSize.h*this.buffer){this.shiftRow(false);}else{break;}}}}},getGridBounds:function(){var bottom=this.grid.length-1;var bottomLeftTile=this.grid[bottom][0];var right=this.grid[0].length-1;var topRightTile=this.grid[0][right];return new OpenLayers.Bounds(bottomLeftTile.bounds.left,bottomLeftTile.bounds.bottom,topRightTile.bounds.right,topRightTile.bounds.top);},_initTiles:function(){var viewSize=this.map.getSize();var bounds=this.map.getExtent();var extent=this.map.getMaxExtent();var resolution=this.map.getResolution();var tilelon=resolution*this.tileSize.w;var tilelat=resolution*this.tileSize.h;var offsetlon=bounds.left-extent.left;var tilecol=Math.floor(offsetlon/tilelon)-this.buffer;var tilecolremain=offsetlon/tilelon-tilecol;var tileoffsetx=-tilecolremain*this.tileSize.w;var tileoffsetlon=extent.left+tilecol*tilelon;var offsetlat=bounds.top-(extent.bottom+tilelat);var tilerow=Math.ceil(offsetlat/tilelat)+this.buffer;var tilerowremain=tilerow-offsetlat/tilelat;var tileoffsety=-tilerowremain*this.tileSize.h;var tileoffsetlat=extent.bottom+tilerow*tilelat;tileoffsetx=Math.round(tileoffsetx);tileoffsety=Math.round(tileoffsety);this.origin=new OpenLayers.Pixel(tileoffsetx,tileoffsety);var startX=tileoffsetx;var startLon=tileoffsetlon;var rowidx=0;do{var row=this.grid[rowidx++];if(!row){row=new Array();this.grid.push(row);}
+tileoffsetlon=startLon;tileoffsetx=startX;var colidx=0;do{var tileBounds=new OpenLayers.Bounds(tileoffsetlon,tileoffsetlat,tileoffsetlon+tilelon,tileoffsetlat+tilelat);var x=tileoffsetx;x-=parseInt(this.map.layerContainerDiv.style.left);var y=tileoffsety;y-=parseInt(this.map.layerContainerDiv.style.top);var px=new OpenLayers.Pixel(x,y);var tile=row[colidx++];if(!tile){tile=this.addTile(tileBounds,px);row.push(tile);}else{tile.moveTo(tileBounds,px,false);}
+tileoffsetlon+=tilelon;tileoffsetx+=this.tileSize.w;}while(tileoffsetlon<=bounds.right+tilelon*this.buffer)
+tileoffsetlat-=tilelat;tileoffsety+=this.tileSize.h;}while(tileoffsetlat>=bounds.bottom-tilelat*this.buffer)
+this.spiralTileLoad();},spiralTileLoad:function(){var tileQueue=new Array();var directions=["right","down","left","up"];var iRow=0;var iCell=-1;var direction=OpenLayers.Util.indexOf(directions,"right");var directionsTried=0;while(directionsTried<directions.length){var testRow=iRow;var testCell=iCell;switch(directions[direction]){case"right":testCell++;break;case"down":testRow++;break;case"left":testCell--;break;case"up":testRow--;break;}
+var tile=null;if((testRow<this.grid.length)&&(testRow>=0)&&(testCell<this.grid[0].length)&&(testCell>=0)){tile=this.grid[testRow][testCell];}
+if((tile!=null)&&(!tile.queued)){tileQueue.unshift(tile);tile.queued=true;directionsTried=0;iRow=testRow;iCell=testCell;}else{direction=(direction+1)%4;directionsTried++;}}
+for(var i=0;i<tileQueue.length;i++){var tile=tileQueue[i]
+tile.draw();tile.queued=false;}},addTile:function(bounds,position){},clearGrid:function(){if(this.grid){for(var iRow=0;iRow<this.grid.length;iRow++){var row=this.grid[iRow];for(var iCol=0;iCol<row.length;iCol++){OpenLayers.Util.clearArray(row[iCol]);}}}},shiftRow:function(prepend){var modelRowIndex=(prepend)?0:(this.grid.length-1);var modelRow=this.grid[modelRowIndex];var resolution=this.map.getResolution();var deltaY=(prepend)?-this.tileSize.h:this.tileSize.h;var deltaLat=resolution*-deltaY;var row=(prepend)?this.grid.pop():this.grid.shift();for(var i=0;i<modelRow.length;i++){var modelTile=modelRow[i];var bounds=modelTile.bounds.clone();var position=modelTile.position.clone();bounds.bottom=bounds.bottom+deltaLat;bounds.top=bounds.top+deltaLat;position.y=position.y+deltaY;row[i].moveTo(bounds,position);}
+if(prepend){this.grid.unshift(row);}else{this.grid.push(row);}},shiftColumn:function(prepend){var deltaX=(prepend)?-this.tileSize.w:this.tileSize.w;var resolution=this.map.getResolution();var deltaLon=resolution*deltaX;for(var i=0;i<this.grid.length;i++){var row=this.grid[i];var modelTileIndex=(prepend)?0:(row.length-1);var modelTile=row[modelTileIndex];var bounds=modelTile.bounds.clone();var position=modelTile.position.clone();bounds.left=bounds.left+deltaLon;bounds.right=bounds.right+deltaLon;position.x=position.x+deltaX;var tile=prepend?this.grid[i].pop():this.grid[i].shift()
+tile.moveTo(bounds,position);if(prepend){this.grid[i].unshift(tile);}else{this.grid[i].push(tile);}}},CLASS_NAME:"OpenLayers.Layer.Grid"});OpenLayers.Layer.MultiMap=OpenLayers.Class.create();OpenLayers.Layer.MultiMap.prototype=OpenLayers.Class.inherit(OpenLayers.Layer.EventPane,OpenLayers.Layer.FixedZoomLevels,{MIN_ZOOM_LEVEL:1,MAX_ZOOM_LEVEL:17,RESOLUTIONS:[9,1.40625,0.703125,0.3515625,0.17578125,0.087890625,0.0439453125,0.02197265625,0.010986328125,0.0054931640625,0.00274658203125,0.001373291015625,0.0006866455078125,0.00034332275390625,0.000171661376953125,0.0000858306884765625,0.00004291534423828125],type:null,initialize:function(name,options){OpenLayers.Layer.EventPane.prototype.initialize.apply(this,arguments);OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this,arguments);},loadMapObject:function(){try{this.mapObject=new MultimapViewer(this.div);}catch(e){}},getWarningHTML:function(){var html="";html+="The MM Layer was unable to load correctly.<br>";html+="<br>";html+="To get rid of this message, select a new BaseLayer "
+html+="in the layer switcher in the upper-right corner.<br>";html+="<br>";html+="Most likely, this is because the MM library";html+=" script was either not correctly included.<br>";html+="<br>";html+="Demmlopers: For help getting this working correctly, ";html+="<a href='http://trac.openlayers.org/wiki/MultiMap' "
+html+="target='_blank'>";html+="click here";html+="</a>";return html;},setMapObjectCenter:function(center,zoom){this.mapObject.goToPosition(center,zoom);},getMapObjectCenter:function(){return this.mapObject.getCurrentPosition();},getMapObjectZoom:function(){return this.mapObject.getZoomFactor();},getMapObjectLonLatFromMapObjectPixel:function(moPixel){moPixel.x=moPixel.x-(this.map.getSize().w/2);moPixel.y=moPixel.y-(this.map.getSize().h/2);return this.mapObject.getMapPositionAt(moPixel);},getMapObjectPixelFromMapObjectLonLat:function(moLonLat){return this.mapObject.geoPosToContainerPixels(moLonLat);},getLongitudeFromMapObjectLonLat:function(moLonLat){return moLonLat.lon;},getLatitudeFromMapObjectLonLat:function(moLonLat){return moLonLat.lat;},getMapObjectLonLatFromLonLat:function(lon,lat){return new MMLatLon(lat,lon);},getXFromMapObjectPixel:function(moPixel){return moPixel.x;},getYFromMapObjectPixel:function(moPixel){return moPixel.y;},getMapObjectPixelFromXY:function(x,y){return new MMPoint(x,y);},CLASS_NAME:"OpenLayers.Layer.MultiMap"});OpenLayers.Layer.Text=OpenLayers.Class.create();OpenLayers.Layer.Text.prototype=OpenLayers.Class.inherit(OpenLayers.Layer.Markers,{location:null,features:null,selectedFeature:null,initialize:function(name,options){OpenLayers.Layer.Markers.prototype.initialize.apply(this,arguments);this.features=new Array();if(this.location!=null){OpenLayers.loadURL(this.location,null,this,this.parseData);}},destroy:function(){this.clearFeatures();this.features=null;OpenLayers.Layer.Markers.prototype.destroy.apply(this,arguments);},parseData:function(ajaxRequest){var text=ajaxRequest.responseText;var lines=text.split('\n');var columns;for(var lcv=0;lcv<(lines.length-1);lcv++){var currLine=lines[lcv].replace(/^\s*/,'').replace(/\s*$/,'');if(currLine.charAt(0)!='#'){if(!columns){columns=currLine.split('\t');}else{var vals=currLine.split('\t');var location=new OpenLayers.LonLat(0,0);var title;var url;var icon,iconSize,iconOffset;var set=false;for(var valIndex=0;valIndex<vals.length;valIndex++){if(vals[valIndex]){if(columns[valIndex]=='point'){var coords=vals[valIndex].split(',');location.lat=parseFloat(coords[0]);location.lon=parseFloat(coords[1]);set=true;}else if(columns[valIndex]=='lat'){location.lat=parseFloat(vals[valIndex]);set=true;}else if(columns[valIndex]=='lon'){location.lon=parseFloat(vals[valIndex]);set=true;}else if(columns[valIndex]=='title')
+title=vals[valIndex];else if(columns[valIndex]=='image'||columns[valIndex]=='icon')
+url=vals[valIndex];else if(columns[valIndex]=='iconSize'){var size=vals[valIndex].split(',');iconSize=new OpenLayers.Size(parseFloat(size[0]),parseFloat(size[1]));}else if(columns[valIndex]=='iconOffset'){var offset=vals[valIndex].split(',');iconOffset=new OpenLayers.Pixel(parseFloat(offset[0]),parseFloat(offset[1]));}else if(columns[valIndex]=='title'){title=vals[valIndex];}else if(columns[valIndex]=='description'){description=vals[valIndex];}}}
+if(set){var data=new Object();if(url!=null){data.icon=new OpenLayers.Icon(url,iconSize,iconOffset);}else{data.icon=OpenLayers.Marker.defaultIcon();if(iconSize!=null){data.icon.setSize(iconSize);}}
+if((title!=null)&&(description!=null)){data['popupContentHTML']='<h2>'+title+'</h2><p>'+description+'</p>';}
+var feature=new OpenLayers.Feature(this,location,data);this.features.push(feature);var marker=feature.createMarker();marker.events.register('click',feature,this.markerClick);this.addMarker(marker);}}}}},markerClick:function(evt){sameMarkerClicked=(this==this.layer.selectedFeature);this.layer.selectedFeature=(!sameMarkerClicked)?this:null;for(var i=0;i<this.layer.map.popups.length;i++){this.layer.map.removePopup(this.layer.map.popups[i]);}
+if(!sameMarkerClicked){this.layer.map.addPopup(this.createPopup());}
+OpenLayers.Event.stop(evt);},clearFeatures:function(){if(this.features!=null){while(this.features.length>0){var feature=this.features[0];OpenLayers.Util.removeItem(this.features,feature);feature.destroy();}}},CLASS_NAME:"OpenLayers.Layer.Text"});OpenLayers.Layer.VirtualEarth=OpenLayers.Class.create();OpenLayers.Layer.VirtualEarth.prototype=OpenLayers.Class.inherit(OpenLayers.Layer.EventPane,OpenLayers.Layer.FixedZoomLevels,{MIN_ZOOM_LEVEL:1,MAX_ZOOM_LEVEL:17,RESOLUTIONS:[1.40625,0.703125,0.3515625,0.17578125,0.087890625,0.0439453125,0.02197265625,0.010986328125,0.0054931640625,0.00274658203125,0.001373291015625,0.0006866455078125,0.00034332275390625,0.000171661376953125,0.0000858306884765625,0.00004291534423828125],type:null,initialize:function(name,options){OpenLayers.Layer.EventPane.prototype.initialize.apply(this,arguments);OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this,arguments);},loadMapObject:function(){var veDiv=OpenLayers.Util.createDiv(this.name);var sz=this.map.getSize();veDiv.style.width=sz.w;veDiv.style.height=sz.h;this.div.appendChild(veDiv);try{this.mapObject=new VEMap(this.name);}catch(e){}
+if(this.mapObject!=null){try{this.mapObject.LoadMap(null,null,this.type);}catch(e){}
+this.mapObject.HideDashboard();}},getWarningHTML:function(){var html="";html+="The VE Layer was unable to load correctly.<br>";html+="<br>";html+="To get rid of this message, select a new BaseLayer "
+html+="in the layer switcher in the upper-right corner.<br>";html+="<br>";html+="Most likely, this is because the VE library";html+=" script was either not correctly included.<br>";html+="<br>";html+="Developers: For help getting this working correctly, ";html+="<a href='http://trac.openlayers.org/wiki/VirtualEarth' "
+html+="target='_blank'>";html+="click here";html+="</a>";return html;},setMapObjectCenter:function(center,zoom){this.mapObject.SetCenterAndZoom(center,zoom);},getMapObjectCenter:function(){return this.mapObject.GetCenter();},getMapObjectZoom:function(){return this.mapObject.GetZoomLevel();},getMapObjectLonLatFromMapObjectPixel:function(moPixel){return this.mapObject.PixelToLatLong(moPixel.x,moPixel.y);},getMapObjectPixelFromMapObjectLonLat:function(moLonLat){return this.mapObject.LatLongToPixel(moLonLat);},getLongitudeFromMapObjectLonLat:function(moLonLat){return moLonLat.Longitude;},getLatitudeFromMapObjectLonLat:function(moLonLat){return moLonLat.Latitude;},getMapObjectLonLatFromLonLat:function(lon,lat){return new VELatLong(lat,lon);},getXFromMapObjectPixel:function(moPixel){return moPixel.x;},getYFromMapObjectPixel:function(moPixel){return moPixel.y;},getMapObjectPixelFromXY:function(x,y){return new Msn.VE.Pixel(x,y);},CLASS_NAME:"OpenLayers.Layer.VirtualEarth"});OpenLayers.Layer.Yahoo=OpenLayers.Class.create();OpenLayers.Layer.Yahoo.prototype=OpenLayers.Class.inherit(OpenLayers.Layer.EventPane,OpenLayers.Layer.FixedZoomLevels,{MIN_ZOOM_LEVEL:0,MAX_ZOOM_LEVEL:15,RESOLUTIONS:[1.40625,0.703125,0.3515625,0.17578125,0.087890625,0.0439453125,0.02197265625,0.010986328125,0.0054931640625,0.00274658203125,0.001373291015625,0.0006866455078125,0.00034332275390625,0.000171661376953125,0.0000858306884765625,0.00004291534423828125],type:null,initialize:function(name,options){OpenLayers.Layer.EventPane.prototype.initialize.apply(this,arguments);OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this,arguments);},loadMapObject:function(){try{this.mapObject=new YMap(this.div,this.type);}catch(e){}},setMap:function(map){OpenLayers.Layer.EventPane.prototype.setMap.apply(this,arguments);this.map.events.register("moveend",this,this.fixYahooEventPane);},fixYahooEventPane:function(){var yahooEventPane=$("ygddfdiv");if(yahooEventPane!=null){if(yahooEventPane.parentNode!=null)
+yahooEventPane.parentNode.removeChild(yahooEventPane);this.map.events.unregister("moveend",this,this.fixYahooEventPane);}},getWarningHTML:function(){var html="";html+="The Yahoo Layer was unable to load correctly.<br>";html+="<br>";html+="To get rid of this message, select a new BaseLayer "
+html+="in the layer switcher in the upper-right corner.<br>";html+="<br>";html+="Most likely, this is because the Yahoo library";html+=" script was either not correctly included.<br>";html+="<br>";html+="Developers: For help getting this working correctly, ";html+="<a href='http://trac.openlayers.org/wiki/Yahoo' "
+html+="target='_blank'>";html+="click here";html+="</a>";return html;},getOLZoomFromMapObjectZoom:function(moZoom){var zoom=null;if(moZoom!=null){zoom=OpenLayers.Layer.FixedZoomLevels.prototype.getOLZoomFromMapObjectZoom.apply(this,[moZoom]);zoom=18-zoom;}
+return zoom;},getMapObjectZoomFromOLZoom:function(olZoom){var zoom=null;if(olZoom!=null){zoom=OpenLayers.Layer.FixedZoomLevels.prototype.getMapObjectZoomFromOLZoom.apply(this,[olZoom]);zoom=18-zoom;}
+return zoom;},setMapObjectCenter:function(center,zoom){this.mapObject.drawZoomAndCenter(center,zoom);},getMapObjectCenter:function(){return this.mapObject.getCenterLatLon();},getMapObjectZoom:function(){return this.mapObject.getZoomLevel();},getMapObjectLonLatFromMapObjectPixel:function(moPixel){return this.mapObject.convertXYLatLon(moPixel);},getMapObjectPixelFromMapObjectLonLat:function(moLonLat){return this.mapObject.convertLatLonXY(moLonLat);},getLongitudeFromMapObjectLonLat:function(moLonLat){return moLonLat.Lon;},getLatitudeFromMapObjectLonLat:function(moLonLat){return moLonLat.Lat;},getMapObjectLonLatFromLonLat:function(lon,lat){return new YGeoPoint(lat,lon);},getXFromMapObjectPixel:function(moPixel){return moPixel.x;},getYFromMapObjectPixel:function(moPixel){return moPixel.y;},getMapObjectPixelFromXY:function(x,y){return new YCoordPoint(x,y);},CLASS_NAME:"OpenLayers.Layer.Yahoo"});OpenLayers.Popup.AnchoredBubble=OpenLayers.Class.create();OpenLayers.Popup.AnchoredBubble.CORNER_SIZE=5;OpenLayers.Popup.AnchoredBubble.prototype=OpenLayers.Class.inherit(OpenLayers.Popup.Anchored,{rounded:false,initialize:function(id,lonlat,size,contentHTML,anchor,closeBox){OpenLayers.Popup.Anchored.prototype.initialize.apply(this,arguments);},draw:function(px){OpenLayers.Popup.Anchored.prototype.draw.apply(this,arguments);this.setContentHTML();this.setRicoCorners(!this.rounded);this.rounded=true;this.setBackgroundColor();this.setOpacity();return this.div;},setSize:function(size){OpenLayers.Popup.Anchored.prototype.setSize.apply(this,arguments);if(this.contentDiv!=null){var contentSize=this.size.clone();contentSize.h-=(2*OpenLayers.Popup.AnchoredBubble.CORNER_SIZE);contentSize.h-=(2*this.padding);this.contentDiv.style.height=contentSize.h+"px";this.setRicoCorners(!this.rounded);this.rounded=true;}},setBackgroundColor:function(color){if(color!=undefined){this.backgroundColor=color;}
+if(this.div!=null){if(this.contentDiv!=null){this.div.style.background="transparent";Rico.Corner.changeColor(this.contentDiv,this.backgroundColor);}}},setOpacity:function(opacity){if(opacity!=undefined){this.opacity=opacity;}
+if(this.div!=null){if(this.contentDiv!=null){Rico.Corner.changeOpacity(this.contentDiv,this.opacity);}}},setBorder:function(border){this.border=0;},setRicoCorners:function(firstTime){var corners=this.getCornersToRound(this.relativePosition);var options={corners:corners,color:this.backgroundColor,bgColor:"transparent",blend:false};if(firstTime){Rico.Corner.round(this.div,options);}else{Rico.Corner.reRound(this.contentDiv,options);this.setBackgroundColor();this.setOpacity();}},getCornersToRound:function(){var corners=['tl','tr','bl','br'];var corner=OpenLayers.Bounds.oppositeQuadrant(this.relativePosition);OpenLayers.Util.removeItem(corners,corner);return corners.join(" ");},CLASS_NAME:"OpenLayers.Popup.AnchoredBubble"});OpenLayers.Layer.KaMap=OpenLayers.Class.create();OpenLayers.Layer.KaMap.prototype=OpenLayers.Class.inherit(OpenLayers.Layer.Grid,{isBaseLayer:true,units:null,resolution:OpenLayers.DOTS_PER_INCH,DEFAULT_PARAMS:{i:'jpeg',map:''},initialize:function(name,url,params,options){var newArguments=new Array();newArguments.push(name,url,params,options);OpenLayers.Layer.Grid.prototype.initialize.apply(this,newArguments);this.params=(params?params:{});if(params){OpenLayers.Util.applyDefaults(this.params,this.DEFAULT_PARAMS);}},getURL:function(bounds){var mapRes=this.map.getResolution();var scale=Math.round((this.map.getScale()*10000))/10000;var cellSize=new OpenLayers.Size(mapRes*this.tileSize.w,mapRes*this.tileSize.h);var pX=Math.round(((bounds.left)/cellSize.w)*this.tileSize.w);var pY=-Math.round(((bounds.top)/cellSize.h)*this.tileSize.h);return this.getFullRequestString({t:pY,l:pX,s:scale});},addTile:function(bounds,position){var url=this.getURL(bounds);return new OpenLayers.Tile.Image(this,position,bounds,url,this.tileSize);},_initTiles:function(){var viewSize=this.map.getSize();var bounds=this.map.getExtent();var extent=this.map.getMaxExtent();var resolution=this.map.getResolution();var tilelon=resolution*this.tileSize.w;var tilelat=resolution*this.tileSize.h;var offsetlon=bounds.left;var tilecol=Math.floor(offsetlon/tilelon);var tilecolremain=offsetlon/tilelon-tilecol;var tileoffsetx=-tilecolremain*this.tileSize.w;var tileoffsetlon=tilecol*tilelon;var offsetlat=bounds.top;var tilerow=Math.ceil(offsetlat/tilelat);var tilerowremain=tilerow-offsetlat/tilelat;var tileoffsety=-(tilerowremain+1)*this.tileSize.h;var tileoffsetlat=tilerow*tilelat;tileoffsetx=Math.round(tileoffsetx);tileoffsety=Math.round(tileoffsety);this.origin=new OpenLayers.Pixel(tileoffsetx,tileoffsety);var startX=tileoffsetx;var startLon=tileoffsetlon;var rowidx=0;do{var row;row=this.grid[rowidx++];if(!row){row=new Array();this.grid.push(row);}
+tileoffsetlon=startLon;tileoffsetx=startX;var colidx=0;do{var tileBounds=new OpenLayers.Bounds(tileoffsetlon,tileoffsetlat,tileoffsetlon+tilelon,tileoffsetlat+tilelat);var x=tileoffsetx;x-=parseInt(this.map.layerContainerDiv.style.left);var y=tileoffsety;y-=parseInt(this.map.layerContainerDiv.style.top);var px=new OpenLayers.Pixel(x,y);var tile;tile=row[colidx++];if(!tile){tile=this.addTile(tileBounds,px);row.push(tile);}else{tile.moveTo(tileBounds,px,false);}
+tileoffsetlon+=tilelon;tileoffsetx+=this.tileSize.w;}while(tileoffsetlon<=bounds.right+tilelon*this.buffer)
+tileoffsetlat-=tilelat;tileoffsety+=this.tileSize.h;}while(tileoffsetlat>=bounds.bottom-tilelat*this.buffer)
+this.spiralTileLoad();},CLASS_NAME:"OpenLayers.Layer.KaMap"});OpenLayers.Layer.MapServer=OpenLayers.Class.create();OpenLayers.Layer.MapServer.prototype=OpenLayers.Class.inherit(OpenLayers.Layer.Grid,{DEFAULT_PARAMS:{mode:"map",map_imagetype:"png"},initialize:function(name,url,params){var newArguments=new Array();params=OpenLayers.Util.upperCaseObject(params);newArguments.push(name,url,params);OpenLayers.Layer.Grid.prototype.initialize.apply(this,newArguments);if(arguments.length>0){OpenLayers.Util.applyDefaults(this.params,OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS));}},isBaseLayer:function(){return(this.params.TRANSPARENT!='true');},clone:function(name,params){var mergedParams={};OpenLayers.Util.extend(mergedParams,this.params);OpenLayers.Util.extend(mergedParams,params);var obj=new OpenLayers.Layer.MapServer(name,this.url,mergedParams);obj.setTileSize(this.tileSize);return obj;},addTile:function(bounds,position){var url=this.getURL(bounds);return new OpenLayers.Tile.Image(this,position,bounds,url,this.tileSize);},getURL:function(bounds){var url=this.getFullRequestString({mapext:bounds.toBBOX().replace(/,/g,"+"),imgext:bounds.toBBOX().replace(/,/g,"+"),map_size:this.tileSize.w+'+'+this.tileSize.h,imgx:this.tileSize.w/2,imgy:this.tileSize.h/2,imgxy:this.tileSize.w+"+"+this.tileSize.h});return url;},CLASS_NAME:"OpenLayers.Layer.MapServer"});OpenLayers.Layer.WFS=OpenLayers.Class.create();OpenLayers.Layer.WFS.prototype=OpenLayers.Class.inherit(OpenLayers.Layer.Grid,OpenLayers.Layer.Markers,{isBaseLayer:false,buffer:1,featureClass:OpenLayers.Feature.WFS,tileClass:OpenLayers.Tile.WFS,DEFAULT_PARAMS:{service:"WFS",version:"1.0.0",request:"GetFeature",typename:"docpoint"},initialize:function(name,url,params,options){var newArguments=new Array();params=OpenLayers.Util.upperCaseObject(params);newArguments.push(name,url,params,options);OpenLayers.Layer.Grid.prototype.initialize.apply(this,newArguments);var newArguments=new Array();newArguments.push(name,options);OpenLayers.Layer.Markers.prototype.initialize.apply(this,newArguments);OpenLayers.Util.applyDefaults(this.params,OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS));},destroy:function(){OpenLayers.Layer.Grid.prototype.destroy.apply(this,arguments);OpenLayers.Layer.Markers.prototype.destroy.apply(this,arguments);},setMap:function(map){OpenLayers.Layer.Grid.prototype.setMap.apply(this,arguments);OpenLayers.Layer.Markers.prototype.setMap.apply(this,arguments);},moveTo:function(bounds,zoomChanged,dragging){OpenLayers.Layer.Grid.prototype.moveTo.apply(this,arguments);OpenLayers.Layer.Markers.prototype.moveTo.apply(this,arguments);},clone:function(obj){if(obj==null){obj=new OpenLayers.Layer.WFS(this.name,this.url,this.params,this.options);}
+obj=OpenLayers.Layer.Grid.prototype.clone.apply(this,[obj]);return obj;},addTile:function(bounds,position){var urls=new Array();urls.push(this.getFullRequestString());if(this.urls!=null){for(var i=0;i<this.urls.length;i++){urls.push(this.getFullRequestString(null,this.urls[i]));}}
+return new this.tileClass(this,position,bounds,urls,this.tileSize);},mergeNewParams:function(newParams){var upperParams=OpenLayers.Util.upperCaseObject(newParams);var newArguments=[upperParams];OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(this,newArguments);if(this.grid!=null){this._initTiles();}},getFullRequestString:function(newParams){var projection=this.map.getProjection();this.params.SRS=(projection=="none")?null:projection;return OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(this,arguments);},getFeature:function(featureID){var foundFeature=null;if(featureID!=null){if(this.grid){for(var iRow=0;iRow<this.grid.length;iRow++){var row=this.grid[iRow];for(var iCol=0;iCol<row.length;iCol++){var tile=row[iCol];for(var i=0;i<tile.features.length;i++){var feature=tile.features[i];if(feature.id==featureID){foundFeature=feature;}}}}}}
+return foundFeature;},CLASS_NAME:"OpenLayers.Layer.WFS"});OpenLayers.Layer.WMS=OpenLayers.Class.create();OpenLayers.Layer.WMS.prototype=OpenLayers.Class.inherit(OpenLayers.Layer.Grid,{DEFAULT_PARAMS:{service:"WMS",version:"1.1.1",request:"GetMap",styles:"",exceptions:"application/vnd.ogc.se_inimage",format:"image/jpeg"},reproject:true,initialize:function(name,url,params,options){var newArguments=new Array();params=OpenLayers.Util.upperCaseObject(params);newArguments.push(name,url,params,options);OpenLayers.Layer.Grid.prototype.initialize.apply(this,newArguments);OpenLayers.Util.applyDefaults(this.params,OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS));if(options==null||options.isBaseLayer==null){this.isBaseLayer=((this.params.TRANSPARENT!="true")&&(this.params.TRANSPARENT!=true));}},destroy:function(){OpenLayers.Layer.Grid.prototype.destroy.apply(this,arguments);},clone:function(obj){if(obj==null){obj=new OpenLayers.Layer.WMS(this.name,this.url,this.params,this.options);}
+obj=OpenLayers.Layer.Grid.prototype.clone.apply(this,[obj]);return obj;},getURL:function(bounds){return this.getFullRequestString({BBOX:bounds.toBBOX(),WIDTH:this.tileSize.w,HEIGHT:this.tileSize.h});},addTile:function(bounds,position){url=this.getURL(bounds);return new OpenLayers.Tile.Image(this,position,bounds,url,this.tileSize);},mergeNewParams:function(newParams){var upperParams=OpenLayers.Util.upperCaseObject(newParams);var newArguments=[upperParams];OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(this,newArguments);if(this.map!=null){this._initTiles();}},getFullRequestString:function(newParams){var projection=this.map.getProjection();this.params.SRS=(projection=="none")?null:projection;return OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(this,arguments);},CLASS_NAME:"OpenLayers.Layer.WMS"});OpenLayers.Layer.WorldWind=OpenLayers.Class.create();OpenLayers.Layer.WorldWind.prototype=OpenLayers.Class.inherit(OpenLayers.Layer.Grid,{DEFAULT_PARAMS:{},isBaseLayer:true,lzd:null,zoomLevels:null,initialize:function(name,url,lzd,zoomLevels,params,options){this.lzd=lzd;this.zoomLevels=zoomLevels;var newArguments=new Array();newArguments.push(name,url,params,options);OpenLayers.Layer.Grid.prototype.initialize.apply(this,newArguments);this.params=(params?params:{});if(params){OpenLayers.Util.applyDefaults(this.params,this.DEFAULT_PARAMS);}},addTile:function(bounds,position){if(this.map.getResolution()<=(this.lzd/512)&&this.getZoom()<=this.zoomLevels){var url=this.getURL(bounds);return new OpenLayers.Tile.Image(this,position,bounds,url,this.tileSize);}else{return new OpenLayers.Tile.Image(this,position,bounds,OpenLayers.Util.getImagesLocation()+"blank.gif",this.tileSize);}},getZoom:function(){var zoom=this.map.getZoom();var extent=this.map.getMaxExtent();zoom=zoom-Math.log(this.maxResolution/(this.lzd/512))/Math.log(2);return zoom;},getURL:function(bounds){var zoom=this.getZoom();var extent=this.map.getMaxExtent();var deg=this.lzd/Math.pow(2,this.getZoom());var x=Math.floor((bounds.left-extent.left)/deg);var y=Math.floor((bounds.bottom-extent.bottom)/deg);if(this.map.getResolution()<=(this.lzd/512)&&this.getZoom()<=this.zoomLevels){return this.getFullRequestString({L:zoom,X:x,Y:y});}else{return OpenLayers.Util.getImagesLocation()+"blank.gif";}},CLASS_NAME:"OpenLayers.Layer.WorldWind"});OpenLayers.Layer.WMS.Untiled=OpenLayers.Class.create();OpenLayers.Layer.WMS.Untiled.prototype=OpenLayers.Class.inherit(OpenLayers.Layer.HTTPRequest,{DEFAULT_PARAMS:{service:"WMS",version:"1.1.1",request:"GetMap",styles:"",exceptions:"application/vnd.ogc.se_inimage",format:"image/jpeg"},reproject:true,ratio:2,tile:null,initialize:function(name,url,params,options){var newArguments=new Array();params=OpenLayers.Util.upperCaseObject(params);newArguments.push(name,url,params,options);OpenLayers.Layer.HTTPRequest.prototype.initialize.apply(this,newArguments);OpenLayers.Util.applyDefaults(this.params,OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS));if((options==null)||(options.isBaseLayer==null)){this.isBaseLayer=((this.params.TRANSPARENT!="true")&&(this.params.TRANSPARENT!=true));}},destroy:function(){this.tile.destroy();this.tile=null;OpenLayers.Layer.HTTPRequest.prototype.destroy.apply(this,arguments);},clone:function(obj){if(obj==null){obj=new OpenLayers.Layer.WMS.Untiled(this.name,this.url,this.params,this.options);}
+obj=OpenLayers.Layer.HTTPRequest.prototype.clone.apply(this,[obj]);return obj;},setMap:function(map){OpenLayers.Layer.HTTPRequest.prototype.setMap.apply(this,arguments);},moveTo:function(bounds,zoomChanged,dragging){OpenLayers.Layer.HTTPRequest.prototype.moveTo.apply(this,arguments);if(bounds==null){bounds=this.map.getExtent();}
+var firstRendering=(this.tile==null);var outOfBounds=(!firstRendering&&!this.tile.bounds.containsBounds(bounds));if(zoomChanged||firstRendering||(!dragging&&outOfBounds)){if(this.tile){this.tile.clear();}
+var center=bounds.getCenterLonLat();var tileWidth=bounds.getWidth()*this.ratio;var tileHeight=bounds.getHeight()*this.ratio;var tileBounds=new OpenLayers.Bounds(center.lon-(tileWidth/2),center.lat-(tileHeight/2),center.lon+(tileWidth/2),center.lat+(tileHeight/2));var tileSize=this.map.getSize();tileSize.w=tileSize.w*this.ratio;tileSize.h=tileSize.h*this.ratio;var url=this.getURL(tileBounds);var ul=new OpenLayers.LonLat(tileBounds.left,tileBounds.top);var pos=this.map.getLayerPxFromLonLat(ul);if(this.tile&&!this.tile.size.equals(tileSize)){this.tile.destroy();this.tile=null;}
+if(!this.tile){this.tile=new OpenLayers.Tile.Image(this,pos,tileBounds,url,tileSize);this.tile.draw();}else{this.tile.moveTo(tileBounds,pos);}}},getURL:function(bounds){var tileSize=this.map.getSize();tileSize.w=tileSize.w*this.ratio;tileSize.h=tileSize.h*this.ratio;return this.getFullRequestString({'BBOX':bounds.toBBOX(),'WIDTH':tileSize.w,'HEIGHT':tileSize.h});},setUrl:function(newUrl){OpenLayers.Layer.HTTPRequest.prototype.setUrl.apply(this,arguments);this.moveTo();},mergeNewParams:function(newParams){var upperParams=OpenLayers.Util.upperCaseObject(newParams);var newArguments=[upperParams];OpenLayers.Layer.HTTPRequest.prototype.mergeNewParams.apply(this,newArguments);this.moveTo();},getFullRequestString:function(newParams){var projection=this.map.getProjection();this.params.SRS=(projection=="none")?null:projection;return OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(this,arguments);},CLASS_NAME:"OpenLayers.Layer.WMS.Untiled"});
\ No newline at end of file


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/build/OpenLayers.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/build/README.txt
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/build/README.txt	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/build/README.txt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,14 @@
+
+## HowTo: Build & deploy "Shrunk" Single File Library version of OpenLayers ##
+
+ * Build:
+
+     cd build
+     ./build.sh
+     cd ..
+
+ * Upload the result to the server: e.g.
+
+  scp build/OpenLayers.js openlayers at openlayers.org:openlayers.org/htdocs/code/
+
+


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/build/README.txt
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/build/build.sh
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/build/build.sh	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/build/build.sh	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,44 @@
+#!/bin/sh
+
+#
+# Script to build compressed single file version of OpenLayers library
+#
+
+OUTPUT_FILENAME=OpenLayers.js
+TMP_OUTPUT_FILENAME=tmp.${OUTPUT_FILENAME}
+
+TOOLS_DIR=../tools
+
+if [ "$1" != "" ]; then
+  CFG_FILENAME="$1.cfg"
+else
+  CFG_FILENAME=library.cfg
+fi
+
+SRC_DIR=../lib
+
+CMD_MERGE_JS=${TOOLS_DIR}/mergejs.py
+
+CMD_SHRINKSAFE=${TOOLS_DIR}/shrinksafe.py
+CMD_JSMIN=${TOOLS_DIR}/jsmin.py
+
+LICENSE_HEADER_FILENAME=license.txt
+
+
+## Generate "fat" single file library version
+${CMD_MERGE_JS} -c ${CFG_FILENAME} ${TMP_OUTPUT_FILENAME} ${SRC_DIR}
+
+
+## Compress ("shrink") the single file library version
+
+echo
+echo Shrinking and post-processing...
+# (We also append the license header here.)
+cat ${LICENSE_HEADER_FILENAME} > ${OUTPUT_FILENAME}
+${CMD_JSMIN} <${TMP_OUTPUT_FILENAME} >> ${OUTPUT_FILENAME}
+
+echo Cleaning up...
+rm $TMP_OUTPUT_FILENAME
+
+echo
+echo Done.


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/build/build.sh
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/build/docs.sh
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/build/docs.sh	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/build/docs.sh	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+rm ../doc/reference.html
+CLASSES="Map Layer Layer.Image Layer.HTTPRequest Layer.Grid Layer.WMS Layer.KaMap Layer.EventPane Layer.Google Layer.VirtualEarth Layer.Markers Layer.Text Layer.GeoRSS Layer.Boxes Icon Marker Marker.Box Tile Tile.Image Tile.WFS Control Control.LayerSwitcher Control.MouseDefaults Control.MousePosition Control.MouseToolbar Control.OverviewMap Control.PanZoom Control.PanZoomBar Control.Permalink Control.Scale LonLat Size Pixel Bounds Util Ajax"
+echo "<html>
+  <head>
+    <title>OpenLayers Class Reference Documentation</title>
+    <style type='text/css'> 
+        .type { background-color: #ddd } 
+        td { border: black solid 1px; padding: 3px;  } 
+        table { border-collapse: collapse; margin: 5px 10px; } 
+        .classref { margin-left: 20px; }
+        div.index { float:left;  width: 300px; }
+    </style>
+  </head>
+  <body>" >> ../doc/reference.html
+
+for i in $CLASSES; do
+echo "<div class='index'><a href='#OpenLayers.$i'>OpenLayers.$i</a></div>" >> ../doc/reference.html
+done
+
+echo "<br style='clear:both' />" >> ../doc/reference.html
+
+for i in $CLASSES; do
+cat ../doc/$i.txt | \
+perl -pe 's!^(.*?) -- (.*?) -- (.*?)$!  <tr><td>$1</td><td>$2</td><td>$3</td></tr>!g; 
+  s!^(  \* )?(.*?) -- (.*?)$!  <tr><td>$2</td><td>$3</td></tr>!g;
+  s!^  \* (.*)$!  </table>\n\n<h3>$1</h3>\n  <table>!;
+  s!{OpenLayers.(.*?)\|(.*?)}!<a href="#OpenLayers.$1" title="OpenLayers.$1">\2</a>!g;
+  s!{OpenLayers.(.*?)}!<a href="#OpenLayers.$1" title="OpenLayers.$1">OpenLayers.$1</a>!;
+  s!{([A-Za-z]+?)\|(.+?)}!<span class="type" title="$1">\2<\/span>!g;
+  s!{([A-Za-z]+?)}!<span class="type" title="$1">$1<\/span>!g;
+  s!^\* (.*)$!</table>\n<h2>$1</h2>\n<table>!;
+  s!^OpenLayers\.(.*)$!<h1><a name="OpenLayers.$1">OpenLayers.$1</a></h1><div class="classref">!' \
+>> ../doc/reference.html 
+echo '</table></div>' >> ../doc/reference.html
+done
+echo "  </body>
+</html>" >> ../doc/reference.html


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/build/docs.sh
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/build/library.cfg
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/build/library.cfg	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/build/library.cfg	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,13 @@
+[first]
+OpenLayers/SingleFile.js
+OpenLayers.js
+OpenLayers/BaseTypes.js
+OpenLayers/Util.js
+Rico/Corner.js
+
+[last]
+
+[include]
+
+[exclude]
+OpenLayers/Control/KeyboardDefaults.js


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/build/library.cfg
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/build/license.txt
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/build/license.txt	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/build/license.txt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,45 @@
+/*
+
+  OpenLayers.js -- OpenLayers Map Viewer Library
+
+  Copyright 2005-2006 MetaCarta, Inc., released under a modified BSD license.
+  Please see http://svn.openlayers.org/trunk/openlayers/release-license.txt
+  for the full text of the license.
+
+  Includes compressed code under the following licenses:
+
+  (For uncompressed versions of the code used please see the
+  OpenLayers SVN repository: <http://openlayers.org/>)
+
+*/
+
+/* Contains portions of Prototype.js:
+ *
+ * Prototype JavaScript framework, version 1.4.0
+ *  (c) 2005 Sam Stephenson <sam at conio.net>
+ *
+ *  Prototype is freely distributable under the terms of an MIT-style license.
+ *  For details, see the Prototype web site: http://prototype.conio.net/
+ *
+/*--------------------------------------------------------------------------*/
+
+/**  
+*  
+*  Contains portions of Rico <http://openrico.org/>
+* 
+*  Copyright 2005 Sabre Airline Solutions  
+*  
+*  Licensed under the Apache License, Version 2.0 (the "License"); you
+*  may not use this file except in compliance with the License. You
+*  may obtain a copy of the License at
+*  
+*         http://www.apache.org/licenses/LICENSE-2.0  
+*  
+*  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. 
+*
+**/
+


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/build/license.txt
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/build/lite.cfg
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/build/lite.cfg	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/build/lite.cfg	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,20 @@
+[first]
+OpenLayers/SingleFile.js
+OpenLayers.js
+OpenLayers/BaseTypes.js
+OpenLayers/Util.js
+
+[last]
+
+[include]
+OpenLayers/Events.js
+OpenLayers/Map.js
+OpenLayers/Layer.js
+OpenLayers/Layer/Grid.js
+OpenLayers/Layer/HTTPRequest.js
+OpenLayers/Layer/WMS.js
+OpenLayers/Layer/WMS/Untiled.js
+OpenLayers/Tile.js
+OpenLayers/Tile/Image.js
+
+[exclude]


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/build/lite.cfg
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Ajax.txt
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Ajax.txt	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Ajax.txt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,9 @@
+OpenLayers contains a number of helper functions for working with AJAX. 
+
+* Methods
+  OpenLayers.loadURL({String|uri}, {String|params}, {Object|caller}, {function|onComplete}, {function|onFailure}) -- none -- This function sends an XMLHTTPRequest to a remote site, specified by URI, with the params string given as the params argument. Caller is bound as 'this' to the function specified by either onComplete or onFailure.
+  OpenLayers.parseXMLString({String|text}) -- {AJAXResponse} -- Accepts a block of text, and creates a DOM response document from the text.
+
+* Parameters
+  OpenLayers.ProxyHost -- When initiating an AJAX request, any URL that is absolute is prefixed with OpenLayers.ProxyHost. The ProxyHost is designed to accept a URL and return the content, in an effort to work around cross-browser security restrictions. This defaults to null, but if you wish to access content from another domain that Javascript would restrict access to, you must set this to be the URL which will accept a URL encoded version of the passed URL and return the content at that URL. An example is available as proxy.txt in the examples/ directory.
+  


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Ajax.txt
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Bounds.txt
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Bounds.txt	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Bounds.txt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,21 @@
+OpenLayers.Bounds
+
+This class represents a bounding box. Data stores as left, bottom, right, top floats.
+
+* Constructor
+  OpenLayers.Bounds(left, bottom, right, top) -- Create new Bounds.
+
+* Methods
+  clone() -- {OpenLayers.Bounds} -- Return copy of the current bounds.
+  toString() -- {String} -- Return bounds as string. 
+  toBBOX() -- {String} -- Return bounds as simple string, usable for WMS.
+  getWidth() -- {float} -- Return width of bounds.
+  getHeight() -- {float} -- Return height of bounds.
+  getSize() -- {OpenLayers.Size} -- Return size of bounds.
+  getCenterPixel() -- {OpenLayers.Pixel} -- Return center of bounds as pixel.
+  getCenterLonLat() -- {OpenLayers.LonLat} -- Return center of bounds as LonLat.
+  equals({OpenLayers.Bounds|bounds}) -- {Boolean} -- whether or not the bounds are equivilant.
+  add({float|x}, {float|y}) -- {OpenLayers.Bounds} -- Shift bounds by x and y. 
+  contains({float|x}, {float|y}, {Boolean|inclusive}) -- {Boolean} -- Return Whether or not the passed-in coordinates are within this bounds 
+  containsBounds({OpenLayers.Bounds|bounds}, {Boolean|partial}, {Boolean|inclusive}) -- {Boolean} -- Returns whether or not the passed-in OpenLayers.Bounds object is contained within this bounds. partial indicates whether bounds must be contained or only overlap. Inclusive determines whether border is included.
+  determineQuadrant({OpenLayers.LonLat|lonlat}) -- {String} -- Determine which quadrant the LonLat is in. Returns combination of "b" or "t" and "l" or "r".


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Bounds.txt
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Control.LayerSwitcher.txt
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Control.LayerSwitcher.txt	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Control.LayerSwitcher.txt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,14 @@
+OpenLayers.Control.LayerSwitcher
+
+This control will by default display a small image in the upper right corner of the map. When clicked, this image expands into a control which allows one to select between multiple types of layers, seperated into two types: Base Layers and Overlays. Only one base layer can be selected at a time, and is enforced by a radio button control being used. Multiple overlays can be selected at once, and a checkbox control is visible for this functionality.
+* Constructor
+  OpenLayers.Control.LayerSwitcher({Object|options}?) -- Creates a new control.
+
+* Methods
+  maximizeControl() -- none -- Expand the layer switcher control.
+  minimizeControl() -- none -- Close the layer switcher control.
+
+* Parameters
+  position -- (inherited from {OpenLayers.Control}) {OpenLayers.Pixel} to use as the top-left corner of the control div, relative to the map area.
+  activeColor -- The color to use for the background of the layer switcher div.
+  ascending -- Ascending determines whether layers are added to the layer switcher in ascending or descending order. If ascending is true, the lowest layer is appended to the list first. If ascending is false, the lowest layer is at the very bottom of the LayerSwitcher. Default is true.  


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Control.LayerSwitcher.txt
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Control.MouseDefaults.txt
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Control.MouseDefaults.txt	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Control.MouseDefaults.txt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,19 @@
+OpenLayers.Control.MouseDefaults
+
+Control to set up mouse handling. This includes clicks, double clicks, scrollwheel events, and mouse movement. There are a number of functions which act as the default handlers for these events: if you wish to create your own mouse control which behaves differently, you should create a subclass of MouseDefaults, replacing the functions whose behavior you wish to change.
+
+
+* Constructor
+  OpenLayers.Control() -- Creates a new control. No options available. 
+
+* Methods
+  defaultClick -- none -- This event takes place when a click event occurs on the map.
+  defaultDblClick -- none -- This event re-centers the map on the clicked location.
+  defaultMouseDown -- none -- This event starts a dragging event, so that the map can be dragged. If the shift key is held, it starts a 'zoombox' or rubber-band-zoom action instead.
+  defaultMouseMove -- none -- If a mouseDrag is in operation, this function causes the map to move to follow the cursor. If a zoombox is in place, this increases the size of the zoombox element to match the cursor.
+  defaultMouseUp -- none -- This function is called when a mouseup event is received, and either sets the center of the map to be the final location or zooms to the zoombox if enabled.
+  defaultMouseOut -- none -- This calls defaultMouseUp, so that when you mouse out of the map div, the events are stopped.
+  defaultWheelUp -- none -- Zooms in one level. Called by onWheelEvent
+  defaultWheelDown -- none -- Zooms out one level. Called by onWheelEvent.
+  onWheelEvent -- none -- catches the map wheel event, and handles it, zooming appropriately. 
+  


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Control.MouseDefaults.txt
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Control.MousePosition.txt
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Control.MousePosition.txt	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Control.MousePosition.txt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,32 @@
+OpenLayers.Control.MousePosition
+
+A small control which displays the Longitude and Latitude of the current mouse position, by defualt in the lower right of the map viewport.
+
+* Constructor
+  OpenLayers.Control.MousePosition({Object|options}) -- Creates a new MousePosition control.
+  
+  
+* Parameters
+
+  element -- if not null, div in which to display the MousePosition
+  prefix -- html to precede the longitude value (default: '')
+  separator -- html to separate the longitude and latitude values (default: '<br />')
+  suffix -- html to follow the latitude value (default: '')
+  numdigits -- number of digits to the right of the decimal (default: 5)
+  granularity -- Don't refresh display if mouse has moved more than this (default: 10)
+
+prefix, separator, and suffix are used to format the lon/lat values.
+
+With:
+   prefix = 'Lon:&nbsp;'
+   suffix = '<br />Lat:&nbsp;'
+   suffix = ''
+   nudigits = 3
+Lon/Lat is displayed as:
+    Lon: 95.123
+	Lat: 35.456
+
+If the mouse has never been over the map, Lon/Lat will equal 0/0.  If the mouse is over the map, Lon/Lat will equal the current mouse position.  If the mouse has been moved off the map, Lon/Lat will equal the value displayed at the time the mouse was moved off the map.
+
+If the mouse is moving slowly, the Lon/Lat will refresh continuously.  If the mouse is moving rapidly, the refresh of Lon/Lat will be suspended until the mouse has slowed down or stopped.  (Trying to update the Lon/Lat value while the mouse is in rapid movemement makes the movement of the mouse unacceptably jerky.)
+


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Control.MousePosition.txt
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Control.MouseToolbar.txt
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Control.MouseToolbar.txt	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Control.MouseToolbar.txt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,15 @@
+OpenLayers.Control.MouseToolbar
+
+Control to set up mouse handling. This includes clicks, double clicks, scrollwheel events, and mouse movement. There are a number of functions which act as the default handlers for these events: if you wish to create your own mouse control which behaves differently, you should create a subclass of MouseToolbar, replacing the functions whose behavior you wish to change. Additionally, a toolbar of images can be clicked to start events, as well as using the keyboard shortcuts. Note that if you wish to use the MouseToolbar, you *must* add a 'controls' option to the map constructor, otherwise you will have both MouseDefaults and MouseToolbar on the map, which will not work.
+
+* Constructor
+  OpenLayers.Control.MouseToolbar({OpenLayers.Pixel|position}, {String|direction}) -- Creates a new control. The direction of the control can be either "vertical" or "horizontal". The default is vertical.
+
+* Methods
+  defaultClick -- none -- This event takes place when a click event occurs on the map.
+  defaultDblClick -- none -- This event re-centers the map on the clicked location.
+  defaultMouseDown -- none -- This event starts a dragging event, so that the map can be dragged. If the shift key is held, it starts a 'zoombox' or rubber-band-zoom action instead.
+  defaultMouseMove -- none -- If a mouseDrag is in operation, this function causes the map to move to follow the cursor. If a zoombox is in place, this increases the size of the zoombox element to match the cursor.
+  defaultMouseUp -- none -- This function is called when a mouseup event is received, and either sets the center of the map to be the final location or zooms to the zoombox if enabled.
+  defaultMouseOut -- none -- This calls defaultMouseUp, so that when you mouse out of the map div, the events are stopped.
+  


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Control.MouseToolbar.txt
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Control.PanZoom.txt
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Control.PanZoom.txt	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Control.PanZoom.txt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,9 @@
+OpenLayers.Control.PanZoom
+
+Creates a small toolset for controlling the location of the map with panning and zooming, including four directional arrows, a zoom in and zoom out, and one button which zooms to the max extent of the map.
+
+* Constructor
+  OpenLayers.Control.PanZoom({Object|options}) -- Creates a new panzoom control.
+
+* Parameters
+  slideFactor -- Number of pixels by which to slide the map when a pan arrow is clicked.


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Control.PanZoom.txt
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Control.PanZoomBar.txt
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Control.PanZoomBar.txt	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Control.PanZoomBar.txt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,6 @@
+OpenLayers.Control.PanZoom
+
+Creates a small toolset for controlling the location of the map with panning and zooming, including four directional arrows, a zoom in and zoom out, and a bar on which a slider can be moved to zoom in or out by multiple stops at once.
+
+* Constructor
+  OpenLayers.Control.PanZoomBar() -- Creates a new panzoombar control.


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Control.PanZoomBar.txt
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Control.Permalink.txt
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Control.Permalink.txt	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Control.Permalink.txt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,6 @@
+OpenLayers.Control.Permalink
+
+A small control which updates a "Permalink" to the map every time the viewport changes. This allows users to copy a link to a specfic map view. By default, it places itself in the lower right corner of the map. This control must be added to the map after a baselayer has been set.  
+
+* Constructor
+  OpenLayers.Control.Scale({DOMElement|element}?, {String|base}) -- Creates a new permalink control. The DOMElement is used, if passed, as the element on which the 'href' is set. If you prefer to put this link on an element outside of the map, set this element to an anchor element to have its href updated when the map moves. If you wish to make the link go to some other URL (for example, an editing interface rather than the current URL), the 'base' property can be used, to which the ?lat=&lon=&zoom= will be appended after the map has moved.


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Control.Permalink.txt
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Control.Scale.txt
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Control.Scale.txt	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Control.Scale.txt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,6 @@
+OpenLayers.Control.Scale
+
+A small control which displays an informative scale value, a la "1:50K" , by defualt in the lower right of the map viewport.
+
+* Constructor
+  OpenLayers.Control.Scale({DOMElement|element}?) -- Creates a new scale control. The DOMElement is used, if passed, to put the scale inside -- this allows you to have the scale displayed in some location outside the map. 


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Control.Scale.txt
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Control.txt
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Control.txt	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Control.txt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,9 @@
+OpenLayers.Control
+
+Base class from which controls are derived. Controls are elements of the map used to control things like zoom, layer status, or to provide information to the user about the status of the map, such as scale or copyright information.
+
+* Constructor
+  OpenLayers.Control({Object|options}?) -- Creates a new control. The options here are used to control parameters of the control. The base control defines only "position", which is designed to be an {OpenLayers.Pixel} which is the top-left of the location of the control relative to the map div. 
+
+* Parameters
+  position -- {OpenLayers.Pixel} to use as the top-left corner of the control div, relative to the map area.


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Control.txt
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Icon.txt
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Icon.txt	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Icon.txt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,9 @@
+OpenLayers.Icon
+
+Icon is used as the visual representation of a Marker. Creating such a representation requres up to three parts: An Icon URL, a size, and an offset. The icon URL is a pointer to an image to use as an icon. The size is the size of the image, as an {OpenLayers.Size} object, and the offset is the position of the top left corner of the image in pixels, relative to the latitude and longitude of the Marker.
+
+* Constructor
+  OpenLayers.Icon({String|url}, {OpenLayers.Size|size}, {OpenLayers.Pixel|offset}) -- creates a new icon.
+
+* Methods
+  clone() -- {OpenLayers.Icon} -- Create an exact copy of the icon.


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Icon.txt
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.Boxes.txt
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.Boxes.txt	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.Boxes.txt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,7 @@
+OpenLayers.Layer.Boxes
+
+A subclass of the markers layer, this layer draws boxes using div elements
+instead of icons using images.
+
+* Constructor
+  OpenLayers.Layer.Boxes(name, options) -- Same as OpenLayers.Layer.Markers.


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.Boxes.txt
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.EventPane.txt
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.EventPane.txt	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.EventPane.txt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,6 @@
+OpenLayers.Layer.EventPane
+
+Designed to act as a super-class for layers which need OpenLayers to intercept click events. Google Maps and Microsoft Virtual Earth both use this functionality, in order to allow for actions like the rubber-band-zoom/zoombox functionality. This Layer is not designed to be used on its own, but instead to act as an intermediary between the user and layers which have their own click handlers.
+
+* Constructor
+  OpenLayers.Layer.EventPane(name, options) -- Only designed to be used by subclasses.


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.EventPane.txt
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.GeoRSS.txt
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.GeoRSS.txt	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.GeoRSS.txt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,12 @@
+OpenLayers.Layer.GeoRSS
+
+GeoRSS layer. Pass a name and location of a GeoRSS file on the web, and a marker will be added to the map for each item in the GeoRSS feed. Uses OpenLayers.loadURL to load the XML, parses the data, and adds markers to the map. The GeoRSS layer supports 1.0 and 2.0 in IE and Firefox, and supports 2.0 in Safari. (Ticket #111 explains the reason for this difference.)
+
+
+* Constructor
+  OpenLayers.Layer.GeoRSS(name, url) -- Name and location of GeoRSS XML file.
+
+* Methods
+  parseData(ajaxResponse) -- none -- Callback for the loadURL call. This function parses the XML for the GeoRSS feed, creating markers for the data and adding a popup to each marker. It populates an array of features as 'this.features'.
+  markerClick(Event) -- none -- Action to perform when a marker is clicked.
+  clearFeatures() -- none -- Removes all features and markers from the map.


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.GeoRSS.txt
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.Google.txt
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.Google.txt	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.Google.txt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,16 @@
+OpenLayers.Layer.Google
+
+The Google Layer in OpenLayers allows you to include Google Maps functionality in your OpenLayers maps. In order to use the Google Layer, you must first obtain an API key for Google Maps, and include the Google Maps API script in your page. Once you have done this, 
+
+* Constructor
+  OpenLayers.Layer.Google(name, options?) -- Constructor for Google layer. Accepts name and options.
+
+* Methods
+  getOLLonLatFromGLatLng(GLonLat) -- {OpenLayers.LonLat} -- Convert from GLatLon to OpenLayers LonLat
+  getGLatLngFromOLLonLat({OpenLayers.LonLat|lonlat}) -- GLatLon -- Convert From OpenLayers LonLat to GLatLon
+  getOLPixelFromGPoint(GPoint) -- {OpenLayers.Pixel} -- Convert from GPoint to OpenLayers.Pixel
+  getGPointFromOLPixel({OpenLayers.Pixel|pixel}) -- GPoint -- Create GPoint from OpenLayers Pixel.
+  
+* Parameters
+  type -- one of G_MAP, G_SATELLITE_MAP, G_HYBRID_MAP, used to choose the type of map to display.
+  


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.Google.txt
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.Grid.txt
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.Grid.txt	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.Grid.txt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,31 @@
+OpenLayers.Layer.Grid
+
+The Grid layer is the base layer for almost all image layers. This is a subclass of HTTPRequest.
+
+* Constructor
+  OpenLayers.Layer.Grid(name, url, params, options) -- URL is the base URL to the layer. Params is a set of params to be included in the HTTP Request. Options is a set of options, extending the parameters of the layer.
+
+* Methods
+  getGridBounds() -- {OpenLayers.Bounds} -- return geographic extent of the grid of tiles.
+  _initTiles() -- none -- Used to initialize the tile grid, creating image elements as neccesary and filling them with the correct images.
+  spiralTileLoad() -- none -- Called from _initTiles. Rotates through the grid filling in the images for the grid.
+  clone() -- {OpenLayers.Layer.Grid} -- create a clone of the layer. Subclassed from {OpenLayers.Layer}.
+  setMap(map) -- none -- Set the map property of the layer. Also set the parameters which are inherited from the map. This is also where the tileSize is retrieved from the map. Subclassed from {OpenLayers.Layer}.
+  moveTo({OpenLayers.Bounds|bounds}, {Boolean|zoomChanged}) -- none -- Not implemented here, but the general function called on dragging or setCenter, to move the Layer to a new geographic location. Subclassed from {OpenLayers.Layer}.
+  getResolution() -- {float} -- Return the current resolution of the map, in units/pixel. Subclassed from {OpenLayers.Layer}.
+  getExtent() -- {OpenLayers.Bounds} -- Return the geographic extent of the current map viewport. Subclassed from {OpenLayers.Layer}.
+  getZoomForExtent({OpenLayers.Bounds|bounds}) -- {Integer} -- return the integer zoom which most closely matches the passed bounds. Subclassed from {OpenLayers.Layer}.
+  getLonLatFromViewPortPx({OpenLayers.Pixel|pixel}) -- {OpenLayers.LonLat} -- Returns an OpenLayers.LonLat which is the passed-in view port OpenLayers.Pixel, translated into lon/lat by the layer. Subclassed from {OpenLayers.Layer}.
+  getViewPortPxFromLonLat({OpenLayers.LonLat|lonlat}) -- {OpenLayers.Pixel} -- Returns An OpenLayers.Pixel which is the passed-in OpenLayers.LonLat, translated into view port pixels. Subclassed from {OpenLayers.Layer}.
+
+* Parameters
+  buffer -- Default 2. Number of tiles around the central map area which OpenLayers will preload so that dragging goes more smoothly.
+  * Subclassed from OpenLayers.Layer
+  isBaseLayer -- This determines whether the layer is a base layer or an overlay. Only one Base Layer can be displayed on the map at once. Default is false.
+  projection -- SRS string to describe the layer. If this is set on the map before the Layer is added to the map, it will inherit from the map.
+  maxExtent -- an OpenLayers.Bounds for the layer. This is the maximum geographic extent of the layer. If this is set on the map before the Layer is added to the map, it will inherit from the map.
+  maxResolution -- The number of degrees per pixel at zoom level 0. If this is set on the map before the Layer is added to the map, it will inherit from the map.
+  maxZoomLevel -- The maximum zoom level for the layer. If this is set on the map before the Layer is added to the map, it will inherit from the map.
+  minScale -- The scale at zoom level 0. If this is set on the map before the Layer is added to the map, it will inherit from the map.
+  maxScale -- The scale at the highest zoom level. If this is set on the map before the Layer is added to the map, it will inherit from the map.
+  units -- The units the map is projected into. If this is set on the map before the Layer is added to the map, it will inherit from the map.


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.Grid.txt
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.HTTPRequest.txt
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.HTTPRequest.txt	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.HTTPRequest.txt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,13 @@
+OpenLayers.Layer.HTTPRequest
+
+Sublass of Layer used for services which require initiating multiple HTTPRequests.
+
+* Constructor
+  OpenLayers.Layer.HTTPRequest(name, url, params, options) -- URL is the base URL to the layer. Params is a set of params to be included in the HTTP Request. Options is a set of options, extending the parameters of the layer.
+
+* Methods
+  initResolutions() -- none -- Based on the current minScale/maxScale/maxResolution/numZoomLevel/scales/resolutions parameters, initializes an array of 'zoom levels' as this.resolutions, which are then used as an index into when zooming.
+  getFullRequestString(newParams) -- {String} -- Returns the full request string for a combination of the defaults on this layer and the parameters passed via newParams.
+
+* Options
+  reproject -- If reproject is true, then the layer will alter the bounding boxes of its tiles to be based on the geographic location of the pixel bounds in the base layer. This is important for reprojecting WMS tiles onto something like Google Maps. Tile locations are calculated, and then when creating the tile request, the bounding box is adjusted to match the bounding box of the base layer at that location.    


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.HTTPRequest.txt
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.Image.txt
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.Image.txt	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.Image.txt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,9 @@
+OpenLayers.Layer.Image
+
+The Image Layer allows you to load a single image and use it as a layer in your
+map.
+
+
+* Constructor
+  OpenLayers.Layer(name, url, {OpenLayers.Bounds|bounds}, {OpenLayers.Size|size}, {options}) -- Image Layer constructor requires a name, URL of the image, geographic bounds of the image, pixel size of the image, and an optional options parameter. 
+


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.Image.txt
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.KaMap.txt
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.KaMap.txt	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.KaMap.txt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,7 @@
+OpenLayers.Layer.KaMap
+
+ka-Map is a Mapserver wrapper which performs caching and works around many of the deficiencies with tiling when using Mapserver as a WMS client.
+
+* Construtor
+  OpenLayers.Layer.KaMap(name, url, params, options) -- URL is the url of the tile.php for KaMap.
+


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.KaMap.txt
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.Markers.txt
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.Markers.txt	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.Markers.txt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,13 @@
+OpenLayers.Layer.Markers
+
+The base class for all vector drawing, The OpenLayers.Layer.Markers layer is the primary way to get annotations into your map. 
+
+* Constructor
+  OpenLayers.Layer.Markers(name, options) -- Name and options for layer.
+
+* Methods
+  addMarker({OpenLayers.Marker|Marker}) -- none -- Adds a marker to the layer, sets the map object on the marker, and calls drawMarker on the marker.
+  removeMarker({OpenLayers.Marker|Marker}) -- none -- Remove the marker from the layer and map.
+  clearMarkers() -- none -- Remove all markers from the layer with removeMarker.
+  redraw() -- none -- Redraw all markers. Used so that markers can change their position when a base layer changes.
+  drawMarker -- none -- Call marker.draw(px), which passes the Pixel of the latitude/longitude in the map viewport to the marker. adds the marker to the div, and marks the marker as drawn.


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.Markers.txt
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.Text.txt
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.Text.txt	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.Text.txt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,10 @@
+OpenLayers.Layer.Text
+
+Subclass of Markers layer which retrieves CSV file from the web, and parses content to create markers and popups.
+
+* Constructor
+  OpenLayers.Layer.Text(name, options) -- Pass in name, and set location: 'URL of CSV file' in the options hash.
+
+* Methods
+  parseData(ajaxRequest) -- none -- Parses returned CSV file. 
+


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.Text.txt
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.VirtualEarth.txt
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.VirtualEarth.txt	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.VirtualEarth.txt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,14 @@
+OpenLayers.Layer.VirtualEarth
+
+The Virtual Earth Layer in OpenLayers allows you to include Virtual Earth Maps in your OpenLayers maps. In order to use the Virtual Earth Layer, you must first include the Virtual Earth API script in your page. Once you have done this, simply add the layer to your map.
+
+* Constructor
+  OpenLayers.Layer.VirtualEarth(name) -- Constructor for Virtual Earth layer. Accepts name.
+
+* Methods
+  getOLLonLatFromVELatLng(VELatLon) -- {OpenLayers.LonLat} -- Convert from VELatLon to OpenLayers LonLat
+  getVELatLngFromOLLonLat({OpenLayers.LonLat|lonlat}) -- VELatLon -- Convert From OpenLayers LonLat to VELatLon
+  getOLPixelFromVEPoint(Msn.VE.Pixel) -- {OpenLayers.Pixel} -- Convert from VEPoint to OpenLayers.Pixel
+  getGPointFromOLPixel({OpenLayers.Pixel|pixel}) -- Msn.VE.Pixel -- Create Msvn.VE.Pixel from OpenLayers Pixel.
+  
+  


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.VirtualEarth.txt
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.WMS.txt
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.WMS.txt	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.WMS.txt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,15 @@
+OpenLayers.Layer.WMS
+
+The WMS layer allows one to connect to a WMS server to obtain images. Params are available as a hash on the layer object, however, all keys are upper-cased automatically for maximum compatibility with WMS servers, so a parameter which is set as 'layers' will be uppercased to LAYERS. Where 'layer' is your Javscript variable for the layer, layer.params.LAYERS is the value of the layers param as set by the constructor.
+
+If you wish to change params, you should use mergeNewParams, rather than accessing the parameters directly.
+
+* Constructor
+  OpenLayers.Layer.WMS(name, url, params, options) -- URL is the base URL to the layer. Params is a set of params to be included in the HTTP Request. Options is a set of options, extending the parameters of the layer.
+
+* Methods
+  getURL({OpenLayers.Bounds|bounds}) -- {String} -- Returns a WMS URL for the given bounds based on the properties of the layer.
+  All other methods are inherited from {OpenLayers.Layer.Grid}
+  
+* Options 
+  isBaseLayer -- WMS layers default to being base layers unless one of their parameters is 'transparent':'true'.


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.WMS.txt
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.txt
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.txt	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.txt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,40 @@
+OpenLayers.Layer
+
+Layers are the most important and central aspect of OpenLayers. They are the 
+core functionality for the software: Everything that ends up on the map comes
+from a layer. The base layer class is designed to be subclassed by any number
+of different layers.
+
+* Constructor
+  OpenLayers.Layer(name, options) -- All layers have a name as their first param. This name is used to display the layer in the LayerSwitcher, and for other purposes. The options parameter is used to replace any parameters of the Layer which need to be different.
+
+* Methods
+  clone() -- {OpenLayers.Layer} -- create a clone of the layer.
+  setName({String|name}) -- none -- Set the name of the layer to something different.
+  moveTo({OpenLayers.Bounds|bounds}, {Boolean|zoomChanged}) -- none -- Not implemented here, but the general function called on dragging or setCenter, to move the Layer to a new geographic location.
+  setMap(map) -- none -- Set the map property of the layer. Also set the parameters which are inherited from the map.
+  getVisibility() -- {Boolean} -- Return true or false based on visibility of the layer.
+  setVisibility({Boolean|visible}) -- none -- Set the layer visibility, and trigger the appropriate events.
+  getResolution() -- {float} -- Return the current resolution of the map, in units/pixel.
+  getExtent() -- {OpenLayers.Bounds} -- Return the geographic extent of the current map viewport.
+  getZoomForExtent({OpenLayers.Bounds|bounds}) -- {Integer} -- return the integer zoom which most closely matches the passed bounds.
+  getLonLatFromViewPortPx({OpenLayers.Pixel|pixel}) -- {OpenLayers.LonLat} -- Returns an OpenLayers.LonLat which is the passed-in view port OpenLayers.Pixel, translated into lon/lat by the laye
+  getViewPortPxFromLonLat({OpenLayers.LonLat|lonlat}) -- {OpenLayers.Pixel} -- Returns An OpenLayers.Pixel which is the passed-in OpenLayers.LonLat, translated into view port pixels
+  addOptions({options})) -- none -- Change the options on the layer. This is the supported way to change properties of the layer. 
+  
+* Parameters
+  displayOutsideMaxExtent -- Determine whether images or data are loaded outside the maxExtent. Default is false.
+  isBaseLayer -- This determines whether the layer is a base layer or an overlay. Only one Base Layer can be displayed on the map at once. Default is false.
+  projection -- SRS string to describe the layer. If this is set on the map before the Layer is added to the map, it will inherit from the map.
+  maxExtent -- an OpenLayers.Bounds for the layer. This is the maximum geographic extent of the layer. If this is set on the map before the Layer is added to the map, it will inherit from the map.
+  maxResolution -- The number of degrees per pixel at zoom level 0. If this is set on the map before the Layer is added to the map, it will inherit from the map.
+  maxZoomLevel -- The maximum zoom level for the layer. If this is set on the map before the Layer is added to the map, it will inherit from the map.
+  minScale -- The scale at zoom level 0. If this is set on the map before the Layer is added to the map, it will inherit from the map.
+  maxScale -- The scale at the highest zoom level. If this is set on the map before the Layer is added to the map, it will inherit from the map.
+  units -- The units the map is projected into. If this is set on the map before the Layer is added to the map, it will inherit from the map.
+  
+
+ 
+ 
+
+  


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Layer.txt
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/LonLat.txt
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/LonLat.txt	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/LonLat.txt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,13 @@
+OpenLayers.LonLat
+
+This class represents a geographic coordinate pair, either projected or unprojected.
+
+* Constructor
+  OpenLayers.LonLat({float|lon}, {float|lat}) -- Create LonLat.
+
+* Methods
+  toString() -- {String} -- Converts to human readable string.
+  toShortString() -- {String} -- Converts to short human readable string.
+  clone() -- {OpenLayers.Size} -- Returns a copy of the pixel.
+  equals({OpenLayers.LonLat|ll}) -- {Boolean} -- Compares two LonLats. 
+  add({int|lon}, {int|lat}) -- {OpenLayers.LonLat} -- add x and y to the current coordinates.


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/LonLat.txt
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Map.txt
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Map.txt	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Map.txt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,77 @@
+OpenLayers.Map
+
+Instantiate class {OpenLayers.Map} in order to create a map. This is the central class in the API. Everything else is auxiliary.
+
+* Constructor
+  OpenLayers.Map(container,  opts?) -- Creates a new map inside of the given HTML container, which is typically a DIV element. The opts variable is an object with various options, as described in the options section below.
+  
+* Methods
+  * Layer Management 
+  addLayer({OpenLayers.Layer|layer}) -- none -- adds a layer to the list currently set for the map. 
+  addLayers([ {OpenLayers.Layer|layer}, {OpenLayers.Layer|layer} ]) -- none -- Adds multiple layers to a map.
+  removeLayer({OpenLayers.Layer|layer}) -- none -- Remove a layer from the map.
+  setBaseLayer({OpenLayers.Layer|layer}) -- none -- Sets a new base layer for the map. The provided layer should have already been added to the map. Changing the base layer causes all other base layers to be turned off, and all overlays to reproject themselves. 
+  
+  * Control Management
+  addControl({OpenLayers.Control|control}) -- none -- Adds a control to the map.
+  
+  * Popup Management
+  addPopup({OpenLayers.Popup|popup}, {Boolean|exclusive}) -- none -- adds a popup to the map. If exclusive is set to true, then all other popups are closed first.
+  removePopup({OpenLayers.Popup|popup}) -- none -- removes an existing popup from the map.
+
+  * Center management
+  setCenter({OpenLayers.LonLat|lonlat}, {int|zoom}) -- none -- Set the center point of the map. This then moves all the layers to the new center location as well, using each layer's 'moveTo' function. The 'zoom' is an integer from 0 to maxZoomLevel.
+  pan({Integer|dx}, {Integer|dy}) -- none -- Allows user to pan by a value of screen pixels
+  
+  * Zoom Management
+  zoomTo({int|zoom}) -- none -- zoom to the given zoom level.
+  zoomIn() -- none -- zoom in one level.
+  zoomOut() -- none -- zoom out one level.
+  zoomToExtent({OpenLayers.Bounds|bounds}) -- none -- Set the map such that the bounds fits within the current viewport area.
+  zoomToMaxExtent() -- none -- Zoom such that the entire bounds of the map is contained in the viewport.
+  zoomToScale({float}) -- none -- Zoom as close to the given scale as possible. Scale can be given as a ratio (1/24000) or as the denominator of the scale value (24000). zoomToScale will find the zoom level which most closely fits the requested scale and set that as the current zoom level.
+
+  * Current Map Information
+  getLonLatFromPixel({OpenLayers.Pixel|pixel}) -- {OpenLayers.LonLat} -- Returns OpenLayers.LonLat corresponding to the given OpenLayers.Pixel, translated into lon/lat by the current base layer
+  getPixelFromLonLat({OpenLayers.LonLat|lonlat}) -- {OpenLayers.Pixel} -- Returns OpenLayers.Pixel corresponding to the given OpenLayers.LonLat, translated by the current base layer  
+  getCenter() -- {OpenLayers.LonLat} -- Returns a LonLat for the current center of the map
+  getZoom() -- {Integer} -- Returns the current zoom level as an integer
+  getExtent() -- {OpenLayers.Bounds} -- Returns a Bounds object which represents the geographic bounds of the current viewPort. 
+  getSize() -- {OpenLayers.Pixel} -- Returns the pixel size of the current map window.
+  getTileSize() -- {OpenLayers.Size} -- Returns tile size currently set for map.
+  getResolution() -- {float} -- Returns the current resolution (units/pixel) of the map
+  getZoomForExtent({OpenLayers.Bounds|bounds}) -- Zoom level in which the given bounds will fit -- zooming to this level and setting the center of the map in the center of the bounds will always fit the given bounds in the map.
+  
+  * Default Map Information 
+  getMaxResolution() -- {float} -- returns The Map's Maximum Resolution, the units/pixel at zoom level 0. The default is 1.40625, to match the approximate MaxResolution used by the commercial providers. 
+  getMaxExtent() -- {OpenLayers.Bounds} -- Return the max extent of the current base layer as defined on the layer. The default maxExtent for OpenLayers is -180,-90,180,90
+  getMaxZoomLevel() -- {int} -- Returns the maximum zoom level that can be reached in the map for the current base layer
+  getMinZoomLevel() -- {int} -- Returns the minimum zoom level that can be reached in the map for the current base layer
+
+* Events
+  addlayer -- a layer is added to the map
+  removelayer -- a layer is removed from the map
+  changelayer -- a layer has some property of it, typically visibility, changed.
+  changebaselayer -- the current base layer changes
+  movestart -- start of a movement in the map       
+  move -- end of movement in a map. this happens while te mouse is dragging as well as for all other events.
+  moveend -- end of movement in a map, but does not happen when the 'dragging' flag is set, as is the case when the move event is in the middle of a drag action.         
+  zoomend -- end of a zoom action
+  mouseover -- map is moused over
+  mouseout -- map is no longer mousedout
+  mousemove -- mouse moves inside map
+  dragstart -- drag action starts
+  dragend -- drag action ends
+  
+* Options:
+  * controls -- an array of control objects to be added to the map. The default is [new OpenLayers.Control.MouseDefaults(), new OpenLayers.Control.PanZoom()] 
+  * projection -- used by WMS layers, should be an SRS identifier.
+  * maxZoomLevel -- The number of zoom levels to use in the map.
+  * maxExtent -- {OpenLayers.Bounds} to be used as the maximum extent of a map. The center of the map can not leave the maxExtent of the map when dragging.
+  * maxResolution -- The units/pixel measurement at zoom level 0. Default is 1.40625
+  * resolutions -- An array of resolutions to be used as an index when zooming. Overrides maxZoomLevel and maxResolution if present.
+  * minScale -- The smallest scale value. minScale is preferred over maxResolution, if present.
+  * maxScale -- The maximum scale the map should include. Overrides maxZoomLevel if present.
+  * units -- The units of the map. Defaults to degrees. Neccesary when using scale in any way.
+  * scales -- An array of scale values. Overrides maxResolution, maxZoomLevel, resolutions, and min/max scale if present.
+  


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Map.txt
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Marker.Box.txt
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Marker.Box.txt	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Marker.Box.txt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,10 @@
+OpenLayers.Marker.Box
+
+A different kind of Marker, designed to be added to a Boxes layer instead of a Markers layer.
+
+* Constructor
+  OpenLayers.Marker.Box({OpenLayers.Bounds|bounds}, borderColor, borderWidth) -- Set up a box. Defaults are red, with a width of 2.
+
+* Methods
+  setBorder(color, width) -- sets properties of border color and width.
+


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Marker.Box.txt
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Marker.txt
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Marker.txt	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Marker.txt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,15 @@
+OpenLayers.Marker
+
+Instantiate class {OpenLayers.Marker} in order to create a marker.
+
+* Constructor
+  OpenLayers.Marker({OpenLayers.LonLat|lonlat}, {OpenLayers.Icon|icon}?  ) -- Creates a new marker object. This object must be added to a Marker layer and will then be displayed. The icon can be left null -- if this is the case, then the default OpenLayers icon will be used.
+  
+* Methods
+  onScreen() -- {boolean} -- Returns whether or not the marker is currently visible on screen.
+  inflate({float}) -- none -- Changes the size of the Icon by a factor of the passed float.
+  display({string}) -- none -- Sets the style.display property of the Icon.
+
+* Events
+  Standard browser events are available.
+


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Marker.txt
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Pixel.txt
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Pixel.txt	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Pixel.txt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,13 @@
+OpenLayers.Pixel
+
+This class represents a screen coordinate in x and y coordinates.
+
+* Constructor
+  OpenLayers.Pixel({float|x}, {float|y}) -- Create Pixel.
+
+* Methods
+  toString() -- {String} -- Converts to human readable string.
+  clone() -- {OpenLayers.Pixel} -- Returns a copy of the pixel.
+  equals({OpenLayers.Pixel|px}) -- {Boolean} -- Tests for equivilance.
+  add({int|x}, {int|y}) -- {OpenLayers.Pixel} -- add x and y to the current coordinates.
+  offset({OpenLayers.Pixel|pixel}) -- {OpenLayers.Pixel} -- Add pixels together.


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Pixel.txt
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Size.txt
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Size.txt	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Size.txt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,11 @@
+OpenLayers.Size
+
+This class represents a width/height pair.
+
+* Constructor
+  OpenLayers.Size({float|h}, {float|w}) -- Create Size.
+
+* Methods
+  toString() -- {String} -- Converts to human readable string.
+  clone() -- {OpenLayers.Size} -- Returns a copy of the pixel.
+  add({int|x}, {int|y}) -- {OpenLayers.Size} -- add x and y to the current coordinates.


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Size.txt
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Tile.Image.txt
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Tile.Image.txt	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Tile.Image.txt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,7 @@
+OpenLayers.Tile.Image
+
+The image tile is designed to hold images for all types of layers, from WMS to KaMap to WorldWind.
+
+* Constructor
+  OpenLayers.Tile.Image({OpenLayers.Layer|layer}, {OpenLayers.Pixel|position}, {OpenLayers.Bounds|bounds}, {String|url}, {OpenLayers.Size|size}) -- Create a new tile, with properties passed.
+  


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Tile.Image.txt
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Tile.WFS.txt
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Tile.WFS.txt	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Tile.WFS.txt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,6 @@
+OpenLayers.Tile.WFS
+
+The WFS tile allows you to load any number of WFS URLs into a single tile. 
+
+* Constructor
+  OpenLayers.Tile.WFS({OpenLayers.Layer|layer}, {OpenLayers.Pixel|position}, {OpenLayers.Bounds|bounds}, {String|urls}, {OpenLayers.Size|size}) -- Create a new tile, with properties passed. The major difference between this tile and other tiles is what it does with results: The WFS tile is designed to parse XML responses, and create features for each item in the response. 


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Tile.WFS.txt
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Tile.txt
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Tile.txt	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Tile.txt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,10 @@
+OpenLayers.Tile
+
+This is a class designed to designate a single tile, however
+it is explicitly designed to do relatively little. Tiles store information
+about themselves -- such as the URL that they are related to, and their 
+size - but do not add themselves to the layer div automatically, for 
+example.
+
+* Constructor
+  OpenLayers.Tile({OpenLayers.Layer|layer}, {OpenLayers.Pixel|position}, {OpenLayers.Bounds|bounds}, {String|url}, {OpenLayers.Size|size}) -- Create a new tile, with properties passed.


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Tile.txt
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Util.txt
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Util.txt	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Util.txt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,8 @@
+OpenLayers.Util
+
+Utility class. Stores a bunch of methods and settings that don't fit anywhere else.
+
+* Options
+
+ OpenLayers.Util.onImageLoadErrorColor -- A string to set img.style.backgroundColor to if the onerror event fires for the image (useful for 500 errors from WMS, for example). 
+ OpenLayers.ImgPath -- A string to be used as the base for images for controls and the like. Should end with '/'. 


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/Util.txt
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/authors.txt
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/authors.txt	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/authors.txt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,17 @@
+OpenLayers core development team
+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+Erik Uzureau
+Christopher R. Schmidt
+Philip Lindsay
+John Frank
+Schuyler Erle
+
+Patch contributors
+------------------
+Corey Puffault
+Tim Schaub
+Jeff Dege
+Sean Gilles
+
+OpenLayers is graciously supported by MetaCarta, Inc.
+<http://www.metacarta.com>.


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/authors.txt
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/customization
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/customization	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/customization	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,49 @@
+Customizing OpenLayers
+======================
+
+OpenLayers is designed to fit many needs -- fitting in alongside all kinds of
+various applications which are currently in use. 
+
+Currently, OpenLayers supports a 'theme' option when creating a map. This 
+theme option allows you to specify the location of a CSS theme which should
+be included.
+
+A default theme is available as an example in the theme/ directory: the setup
+is:
+
+ * theme/
+ * theme/default/
+ * theme/default/style.css
+ * theme/default/img/
+
+Currently, the OpenLayers code does not support class names, and therefore,
+it is not possible to control many aspects of OpenLayers code with CSS 
+classes. However, with this framework in place, we expect to invest time 
+to make existing features and new features use the CSS theming framework
+where apropriate. 
+
+
+Class Naming
+============
+Elements should have class names which are descriptive of the Javascript
+class from which they come. For example, the main layer switcher element
+in the OpenLayers.Control.LayerSwitcher would be classed:
+
+  olControlLayerSwitcher
+
+This would allow users to add to their style.css class in their theme,
+changing, for example: 
+
+::
+  
+  .olControlLayerSwitcher input {
+    width:10px;
+  }
+
+Sub elements of a particular control can add to the class name:
+
+::
+
+  .olControlLayerSwitcherBaseLabel {
+      color: red;
+  }


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/doc/customization
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/baseLayers.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/baseLayers.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/baseLayers.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,83 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <style type="text/css">
+        #map {
+            width: 100%;
+            height: 512px;
+            border: 1px solid black;
+            background-color: blue;
+        }
+    </style>
+
+    <!-- this gmaps key generated for http://openlayers.org/dev/ -->
+    <script src='http://maps.google.com/maps?file=api&amp;v=2&amp;key=ABQIAAAA9XNhd8q0UdwNC7YSO4YZghSPUCi5aRYVveCcVYxzezM4iaj_gxQ9t-UajFL70jfcpquH5l1IJ-Zyyw'></script>
+    <!-- Localhost key -->
+    <!-- <script src='http://maps.google.com/maps?file=api&amp;v=2&amp;key=ABQIAAAAjpkAC9ePGem0lIq5XcMiuhT2yXp_ZAY8_ufC3CFXhHIE1NvwkxTS6gjckBmeABOGXIUiOiZObZESPg'></script>-->
+    <script type="text/javascript" src="http://clients.multimap.com/API/maps/1.1/metacarta_04"></script>
+    <script src='http://dev.virtualearth.net/mapcontrol/v3/mapcontrol.js'></script>
+    <script src="http://api.maps.yahoo.com/ajaxymap?v=3.0&appid=euzuro-openlayers"></script>
+    <script src="../lib/OpenLayers.js"></script>
+    <script type="text/javascript">
+        <!--
+        var lon = 5;
+        var lat = 40;
+        var zoom = 5;
+        var map, markers;
+        var barcelona = new OpenLayers.LonLat(2.13134765625,
+                                              41.37062534198901);
+        var madrid = new OpenLayers.LonLat(-3.6968994140625,
+                                              40.428314208984375);
+
+        function init(){
+            map = new OpenLayers.Map( $('map') );
+
+            var wms = new OpenLayers.Layer.WMS( "OpenLayers WMS", 
+                      "http://labs.metacarta.com/wms/vmap0", 
+                      {layers: 'basic'} );
+            var google = new OpenLayers.Layer.Google( "Google Hybrid" , {type: G_HYBRID_MAP });
+            var ve = new OpenLayers.Layer.VirtualEarth( "VE"); 
+            var yahoo = new OpenLayers.Layer.Yahoo( "Yahoo"); 
+            var mm = new OpenLayers.Layer.MultiMap( "MultiMap"); 
+            
+
+            map.addLayers([wms, google, ve, yahoo, mm]);
+
+            markers = new OpenLayers.Layer.Markers("markers");
+            map.addLayer(markers);
+
+            map.setCenter(new OpenLayers.LonLat(lon, lat), zoom);
+            map.addControl( new OpenLayers.Control.LayerSwitcher() );
+            map.addControl( new OpenLayers.Control.MousePosition() );
+
+        }
+        
+        function add() {
+            var url = 'http://boston.openguides.org/markers/AQUA.png';
+            var sz = new OpenLayers.Size(10, 17);
+            var calculateOffset = function(size) {
+                                return new OpenLayers.Pixel(-(size.w/2), -size.h);
+                             };           
+            var icon = new OpenLayers.Icon(url, sz, null, calculateOffset);
+            marker = new OpenLayers.Marker(barcelona, icon);
+            markers.addMarker(marker);
+
+            marker = new OpenLayers.Marker(madrid, icon.clone());
+            markers.addMarker(marker);
+
+        }
+
+        function remove() {
+            markers.removeMarker(marker);
+        }
+                
+    // -->
+
+    </script>
+  </head>
+  <body onload="init()">
+    <h1>OpenLayers With WMS, Google, VE Example</h1>
+    <div id="map"></div>
+    <div style="background-color:green" onclick="add()"> click to add a marker to the map</div>
+    <div style="background-color:red" onclick="remove()"> click to remove the marker from the map</div>
+  </body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/baseLayers.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/boxes.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/boxes.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/boxes.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,49 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <style type="text/css">
+        #map {
+            width: 512px;
+            height: 512px;
+            border: 1px solid black;
+        }
+    </style>
+    <script src="../lib/OpenLayers.js"></script>
+    <script type="text/javascript">
+        <!--
+        box_extents = [
+            [-10, 50, 5, 60],
+            [-75, 41, -71, 44],
+            [-122.6, 37.6, -122.3, 37.9],
+            [10, 10, 20, 20]
+        ];
+
+        function init(){
+            var map = new OpenLayers.Map('map');
+
+            var ol_wms = new OpenLayers.Layer.WMS( "OpenLayers WMS", 
+                "http://labs.metacarta.com/wms/vmap0?", {layers: 'basic'} );
+
+            var boxes  = new OpenLayers.Layer.Boxes( "Boxes" );
+
+            for (var i = 0; i < box_extents.length; i++) {
+                ext = box_extents[i];
+                bounds = new OpenLayers.Bounds(ext[0], ext[1], ext[2], ext[3]); 
+                box = new OpenLayers.Marker.Box(bounds);
+                box.events.register("click", box, function (e) {
+                    this.setBorder("yellow");
+                });
+                boxes.addMarker(box);
+            }
+
+            map.addLayers([ol_wms, boxes]);
+            map.addControl(new OpenLayers.Control.LayerSwitcher());
+            map.zoomToMaxExtent();
+        }
+        // -->
+    </script>
+  </head>
+  <body onload="init()">
+    <h1>OpenLayers Example</h1>
+    <div id="map"></div>
+  </body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/boxes.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/canvas.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/canvas.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/canvas.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,613 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <style type="text/css">
+        #map {
+            width: 512px;
+            height: 512px;
+            border: 1px solid black;
+        }
+    </style>
+        <script src='http://maps.google.com/maps?file=api&amp;v=2&amp;key=ABQIAAAA9XNhd8q0UdwNC7YSO4YZghSPUCi5aRYVveCcVYxzezM4iaj_gxQ9t-UajFL70jfcpquH5l1IJ-Zyyw'></script>
+    <script src="../lib/OpenLayers.js"></script>
+    <script type="text/javascript">
+        <!--
+        var map, canvas;
+        var click;
+        function init(){
+            map = new OpenLayers.Map('map');
+
+            var ol_wms = new OpenLayers.Layer.WMS( "OpenLayers WMS", 
+                "http://labs.metacarta.com/wms/vmap0?", {layers: 'basic'}); 
+            var g = new OpenLayers.Layer.Google("Google");    
+            canvas = new OpenLayers.Layer.Canvas("Canvas Layer");
+            map.addLayers([ol_wms, g,canvas]);
+            map.addControl(new OpenLayers.Control.LayerSwitcher());
+            // map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+            map.zoomToMaxExtent();
+            canvas.drawLine(new OpenLayers.LonLat(-71,42), new OpenLayers.LonLat(0,0));
+            canvas.setStrokeColor("red");
+            canvas.setStrokeWidth(5);
+            canvas.setAlpha(.5);
+            map.events.register("click", map, function(e) {
+              if (click) {
+                canvas.drawLine(click, this.getLonLatFromPixel(e.xy));
+                click = null;
+                $('status').innerHTML = "";
+              } 
+              else { 
+               click = this.getLonLatFromPixel(e.xy);
+               $('status').innerHTML = "Point 1 stored: "+click+". <a href='#' onclick='click=null;return false'>Reset</a>";
+             }
+           });
+        }
+        function drawIt() { 
+          canvas.drawLine(
+            new OpenLayers.LonLat(parseFloat($('lon1').value), parseFloat($('lat1').value)), 
+            new OpenLayers.LonLat(parseFloat($('lon2').value), parseFloat($('lat2').value))
+          );
+        }
+        // -->
+    </script>
+  </head>
+  <body onload="init()">
+    <h1>OpenLayers Example</h1>
+    <div style="float:right">
+    <br />
+    Start Lon: <input type="text" id="lon1" />
+    Start Lat: <input type="text" id="lat1" /><br />
+    End Lon: <input type="text" id="lon2" />
+    End Lat: <input type="text" id="lat2" /><br />
+    <input type="submit" value="Add line" onclick="drawIt()" />
+    <div id="status"></div>
+    <center>
+<table border="1" cellpadding="0" cellspacing="1">
+<tbody>
+<tr>
+<td bgcolor="#000000">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#000000');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#000033">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#000033');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#000066">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#000066');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#000099">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#000099');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#0000cc">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#0000CC');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#0000ff">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#0000FF');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#330000">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#330000');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#330033">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#330033');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#330066">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#330066');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#330099">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#330099');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#3300cc">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#3300CC');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#3300ff">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#3300FF');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#660000">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#660000');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#660033">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#660033');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#660066">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#660066');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#660099">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#660099');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#6600cc">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#6600CC');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#6600ff">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#6600FF');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+</tr>
+<tr>
+<td bgcolor="#990000">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#990000');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#990033">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#990033');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#990066">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#990066');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#990099">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#990099');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#9900cc">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#9900CC');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#9900ff">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#9900FF');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#cc0000">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#CC0000');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#cc0033">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#CC0033');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#cc0066">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#CC0066');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#cc0099">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#CC0099');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#cc00cc">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#CC00CC');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#cc00ff">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#CC00FF');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#ff0000">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#FF0000');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#ff0033">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#FF0033');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#ff0066">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#FF0066');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#ff0099">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#FF0099');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#ff00cc">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#FF00CC');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#ff00ff">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#FF00FF');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+</tr>
+<tr>
+<td bgcolor="#003300">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#003300');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#003333">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#003333');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#003366">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#003366');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#003399">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#003399');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#0033cc">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#0033CC');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#0033ff">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#0033FF');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#333300">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#333300');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#333333">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#333333');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#333366">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#333366');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#333399">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#333399');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#3333cc">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#3333CC');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#3333ff">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#3333FF');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#663300">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#663300');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#663333">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#663333');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#663366">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#663366');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#663399">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#663399');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#6633cc">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#6633CC');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#6633ff">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#6633FF');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+</tr>
+<tr>
+<td bgcolor="#993300">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#993300');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#993333">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#993333');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#993366">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#993366');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#993399">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#993399');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#9933cc">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#9933CC');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#9933ff">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#9933FF');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#cc3300">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#CC3300');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#cc3333">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#CC3333');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#cc33cc">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#CC33CC');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#cc33ff">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#CC33FF');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#ff3300">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#FF3300');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#ff3333">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#FF3333');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#ff3366">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#FF3366');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#ff3399">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#FF3399');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#ff33cc">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#FF33CC');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#ff33ff">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#FF33FF');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+</tr>
+<tr>
+<td bgcolor="#006600">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#006600');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#006633">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#006633');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#006666">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#006666');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#006699">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#006699');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#0066cc">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#0066CC');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#336699">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#336699');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#3366cc">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#3366CC');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#3366ff">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#3366FF');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#666600">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#666600');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#666633">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#666633');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#666666">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#666666');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#666699">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#666699');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#6666cc">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#6666CC');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#6666ff">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#6666FF');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+</tr>
+<tr>
+<td bgcolor="#996600">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#996600');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#996633">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#996633');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#9966ff">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#9966FF');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#cc6600">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#CC6600');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#cc6633">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#CC6633');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#cc6666">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#CC6666');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#cc6699">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#CC6699');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#cc66cc">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#CC66CC');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#cc66ff">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#CC66FF');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#ff6600">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#FF6600');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#ff6633">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#FF6633');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+</tr>
+<tr>
+<td bgcolor="#009900">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#009900');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#009933">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#009933');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#009966">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#009966');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#009999">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#009999');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#0099cc">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#0099CC');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#0099ff">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#0099FF');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#339900">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#339900');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#339933">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#339933');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#339966">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#339966');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#339999">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#339999');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+<td bgcolor="#3399cc">
+<font size="-3">
+<a href="#" onclick="canvas.setStrokeColor('#3399CC');return false">&nbsp;&nbsp;&nbsp;</a>
+</font>
+</td>
+</tr>
+</table>
+    </div>
+    <div id="map"></div>
+  </body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/canvas.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/ciesin.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/ciesin.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/ciesin.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,40 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <style type="text/css">
+        #map {
+            width: 512px;
+            height: 512px;
+            border: 1px solid black;
+        }
+    </style>
+    <script src="../lib/OpenLayers.js"></script>
+    <script type="text/javascript">
+        <!--
+        function init(){
+            var map = new OpenLayers.Map('map');
+
+            var ol_wms = new OpenLayers.Layer.WMS( "OpenLayers WMS", 
+                "http://labs.metacarta.com/wms/vmap0?", {layers: 'basic'} );
+
+            var jpl_wms = new OpenLayers.Layer.WMS( "Population Density 2000",
+                "http://beta.sedac.ciesin.columbia.edu/mapserver/wms/gpw2000", 
+                {layers: "gpw"});
+            var cie_wms = new OpenLayers.Layer.WMS( "Human Footprint",
+                "http://beta.sedac.ciesin.columbia.edu/mapserver/wms/hfoot", 
+                {layers: "hfoot"});
+
+            jpl_wms.setVisibility(false);
+
+            map.addLayers([ol_wms, jpl_wms, cie_wms]);
+            map.addControl(new OpenLayers.Control.LayerSwitcher());
+            // map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+            map.zoomToMaxExtent();
+        }
+        // -->
+    </script>
+  </head>
+  <body onload="init()">
+    <h1>OpenLayers Example</h1>
+    <div id="map"></div>
+  </body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/ciesin.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/click.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/click.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/click.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,42 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <style type="text/css">
+        #map {
+            width: 512px;
+            height: 512px;
+            border: 1px solid black;
+        }
+    </style>
+    <script src="../lib/OpenLayers.js"></script>
+    <script type="text/javascript">
+        <!--
+        function init(){
+            var map = new OpenLayers.Map('map');
+
+            var ol_wms = new OpenLayers.Layer.WMS( "OpenLayers WMS", 
+                "http://labs.metacarta.com/wms/vmap0?", {layers: 'basic'} );
+
+            var jpl_wms = new OpenLayers.Layer.WMS( "NASA Global Mosaic",
+                "http://wms.jpl.nasa.gov/wms.cgi", 
+                {layers: "modis,global_mosaic"});
+
+            jpl_wms.setVisibility(false);
+
+            map.addLayers([ol_wms, jpl_wms]);
+            map.addControl(new OpenLayers.Control.LayerSwitcher());
+            // map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+            map.zoomToMaxExtent();
+            map.events.register("click", map, function(e) { 
+                var lonlat = map.getLonLatFromViewPortPx(e.xy);
+                alert("You clicked near " + lonlat.lat + " N, " +
+                                          + lonlat.lon + " E");
+            });
+        }
+        // -->
+    </script>
+  </head>
+  <body onload="init()">
+    <h1>OpenLayers Example</h1>
+    <div id="map"></div>
+  </body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/click.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/controls.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/controls.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/controls.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,51 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <style type="text/css">
+        #map {
+            width: 512px;
+            height: 512px;
+            border: 1px solid black;
+        }
+    </style>
+    <script src="../lib/OpenLayers.js"></script>
+    <script type="text/javascript">
+        <!--
+        function init(){
+            var map = new OpenLayers.Map('map', { controls: [] });
+
+            map.addControl(new OpenLayers.Control.PanZoomBar());
+            map.addControl(new OpenLayers.Control.MouseToolbar());
+            map.addControl(new OpenLayers.Control.LayerSwitcher({'ascending':false}));
+            map.addControl(new OpenLayers.Control.Permalink());
+            map.addControl(new OpenLayers.Control.Permalink($('permalink')));
+            map.addControl(new OpenLayers.Control.MousePosition());
+            map.addControl(new OpenLayers.Control.OverviewMap());
+            var ol_wms = new OpenLayers.Layer.WMS( "OpenLayers WMS", 
+                "http://labs.metacarta.com/wms/vmap0",
+                {layers: 'basic'} );
+
+            var jpl_wms = new OpenLayers.Layer.WMS( "NASA Global Mosaic",
+                "http://wms.jpl.nasa.gov/wms.cgi", 
+                {layers: "modis,global_mosaic"});
+
+            var dm_wms = new OpenLayers.Layer.WMS( "DM Solutions Demo",
+                "http://www2.dmsolutions.ca/cgi-bin/mswms_gmap",
+                {layers: "bathymetry,land_fn,park,drain_fn,drainage," +
+                         "prov_bound,fedlimit,rail,road,popplace",
+                 transparent: "true", format: "image/png" });
+
+            jpl_wms.setVisibility(false);
+            dm_wms.setVisibility(false);
+
+            map.addLayers([ol_wms, jpl_wms, dm_wms]);
+            if (!map.getCenter()) map.zoomToMaxExtent();
+        }
+        // -->
+    </script>
+  </head>
+  <body onload="init()">
+    <h1>OpenLayers Example</h1>
+    <a style="float:right" href="" id="permalink">Permalink</a>
+    <div id="map"></div>
+  </body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/controls.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/doubleSetCenter.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/doubleSetCenter.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/doubleSetCenter.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,17 @@
+<html>
+<head>
+<script src="../lib/OpenLayers.js"></script>
+</head>
+<body>
+  <div style="width:100%; height:100%" id="map"></div>
+  <script defer="defer" type="text/javascript">
+    var map = new OpenLayers.Map('map');
+    var wms = new OpenLayers.Layer.WMS( "OpenLayers WMS", 
+        "http://labs.metacarta.com/wms/vmap0", {layers: 'basic'} );
+    map.addLayer(wms);
+    map.setCenter(new OpenLayers.LonLat(100,10));
+    map.setCenter(new OpenLayers.LonLat(1,1));
+  </script>
+</body>
+</html>
+


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/doubleSetCenter.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/example.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/example.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/example.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,51 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <style type="text/css">
+        #map {
+            width: 512px;
+            height: 512px;
+            border: 1px solid black;
+        }
+    </style>
+    <script src="../lib/OpenLayers.js"></script>
+    <script type="text/javascript">
+        <!--
+        function init(){
+            var map = new OpenLayers.Map('map');
+
+            var options = { 
+                            resolutions: [1.40625,0.703125,0.3515625,0.17578125,0.087890625,0.0439453125,0.02197265625,0.010986328125,0.0054931640625,0.00274658203125,0.00137329101]
+            };
+            
+            var ol_wms = new OpenLayers.Layer.WMS( "OpenLayers WMS", 
+                "http://labs.metacarta.com/wms/vmap0?", {layers: 'basic'},
+                options); 
+
+            var options2 = { 
+                            resolutions: [0.17578125,0.087890625,0.0439453125,0.02197265625,0.010986328125,0.0054931640625,0.00274658203125,0.00137329101]
+            };
+            var jpl_wms = new OpenLayers.Layer.WMS( "NASA Global Mosaic",
+                "http://wms.jpl.nasa.gov/wms.cgi", 
+                {layers: "modis,global_mosaic"}, options2);
+
+            var dm_wms = new OpenLayers.Layer.WMS( "DM Solutions Demo",
+                "http://www2.dmsolutions.ca/cgi-bin/mswms_gmap",
+                {layers: "bathymetry,land_fn,park,drain_fn,drainage," +
+                         "prov_bound,fedlimit,rail,road,popplace",
+                 transparent: "true", format: "image/png"},
+                 {minResolution: 0.17578125,
+                  maxResolution: 0.703125});
+
+            map.addLayers([ol_wms, jpl_wms, dm_wms]);
+            map.addControl(new OpenLayers.Control.LayerSwitcher());
+            // map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+            map.zoomToMaxExtent();
+        }
+        // -->
+    </script>
+  </head>
+  <body onload="init()">
+    <h1>OpenLayers Example</h1>
+    <div id="map"></div>
+  </body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/example.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/freemap.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/freemap.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/freemap.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,107 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+
+    <style type="text/css">
+    body { font-family: sans-serif; font-weight: bold; font-size: .8em; }
+    body {
+	border: 0px;
+	margin: 0px;
+	padding: 0px;
+    }
+    #map {
+	width: 65%;
+	height: 100%;
+	border: 0px;
+	padding: 0px;
+    }
+    </style>
+
+    <script src="../lib/OpenLayers.js"></script>
+    <script type="text/javascript">
+      <!--
+	var lat = 900863; 
+	var lon = 235829;
+	var zoom = 6;
+        var map, layer;
+
+        function init(){
+	    // these should be object methods or something
+            map = new OpenLayers.Map( $('map') );
+            var basemap = new OpenLayers.Layer.WMS( "Boston", 
+                "http://boston.freemap.in/cgi-bin/mapserv?",
+                {map: '/www/freemap.in/boston/map/gmaps.map', layers: 'border,water,roads', format: 'png', 'transparent': 'off'},
+                {maxExtent: new OpenLayers.Bounds(33861, 717605, 330846, 1019656), maxResolution: 296985/1024, projection:"EPSG:2805" } );
+            var rapid = new OpenLayers.Layer.WMS( "Rapid Transit", 
+                "http://boston.freemap.in/cgi-bin/mapserv?",
+                {map: '/www/freemap.in/boston/map/mass.map', layers: 'rapid_transit', format: 'png', transparent:'true'} );
+            var buildings = new OpenLayers.Layer.WMS( "Buildings", 
+                "http://boston.freemap.in/cgi-bin/mapserv?",
+                {map: '/www/freemap.in/boston/map/mass.map', layers: 'buildings', format: 'png', transparent:'true'} );
+                
+            map.addLayer(basemap);
+            map.addLayer(rapid);
+            map.addLayer(buildings);
+            map.setCenter(new OpenLayers.LonLat(lon, lat), zoom);
+            map.addControl(new OpenLayers.Control.LayerSwitcher());
+        }
+   function getaddress() {
+      if (!document.getElementById('address').value) {return; }
+                    document.getElementById('status').innerHTML = "Finding address...";
+      var address = document.getElementById('address').value;
+      address = escape(address);
+      addr = "/geocode.cgi?address="+address;
+      var handler = XMLrequest();
+      if (handler) {
+               handler.onreadystatechange=function() {
+                   if (handler.readyState == 4 && handler.status == 200) {
+                   var latlon = handler.responseText;
+                   latlon = latlon.split(",");
+                   if (latlon[1]) {
+                       map.setCenter(new OpenLayers.LonLat(parseFloat(latlon[1]), parseFloat(latlon[0])), 15);
+                       document.getElementById('status').innerHTML = "";
+                   
+                   } else {
+                    document.getElementById('status').innerHTML = "Could not find address, sorry.";
+                   }
+                   }
+               }
+               handler.open("GET", addr, true);
+               handler.send('');
+           }
+   }
+function XMLrequest() {
+xmlhttp={};
+try {
+  xmlhttp=new ActiveXObject("Msxml2.XMLHTTP");
+} catch (e) {
+  try {
+    xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
+  } catch (E) { }
+}
+try {
+  xmlhttp = new XMLHttpRequest();
+} catch (e) {}
+return xmlhttp;
+}
+function setLink() {
+	var link = document.getElementById("link");
+	var center = map.getCenter();
+	var zoom = map.getZoom();
+	link.innerHTML="http://boston.freemap.in/?zoom="+zoom+"&amp;lat="+center.lat+"&amp;lon="+center.lon;
+}	
+//      -->
+    </script>
+  </head>
+
+<body onload="init()">
+<div id="right" style="float:right;width:30%;padding:10px;" ><h1>Boston Free Map</h1><!--Search: <input type="text" id='address' name="address"><input type="submit" value="Go!" onclick="javascript:getaddress()"/>--><div id="status" style="height:20px;min-height:20px;">&nbsp;</div>
+    <div>Map powered by <a href="http://www.openlayers.org/">OpenLayers</a>
+	 and <a href="http://mapserver.gis.umn.edu/">MapServer</a>.
+         Data downloaded from
+	 <a href="http://www.mass.gov/mgis/">Office of Geographic and Environmental Information (MassGIS)</a>.</div>
+	 <!--<a onclick="setLink(); return false" href="#" id="update">Update Link</a>
+	 <div id="link"></div>-->
+	 </div>
+    <div id="map"></div>
+</body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/freemap.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/fullScreen.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/fullScreen.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/fullScreen.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,40 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <style type="text/css">
+        #map {
+            width: 100%;
+            height: 100%;
+            border: 1px solid black;
+        }
+    </style>
+    <script src="../lib/OpenLayers.js"></script>
+    <script type="text/javascript">
+        <!--
+        function init(){
+            var map = new OpenLayers.Map('map');
+
+            var ol_wms = new OpenLayers.Layer.WMS( "OpenLayers WMS", 
+                "http://labs.metacarta.com/wms/vmap0?", {layers: 'basic'}); 
+
+            var jpl_wms = new OpenLayers.Layer.WMS( "NASA Global Mosaic",
+                "http://wms.jpl.nasa.gov/wms.cgi", 
+                {layers: "modis,global_mosaic"});
+
+            var dm_wms = new OpenLayers.Layer.WMS( "DM Solutions Demo",
+                "http://www2.dmsolutions.ca/cgi-bin/mswms_gmap",
+                {layers: "bathymetry,land_fn,park,drain_fn,drainage," +
+                         "prov_bound,fedlimit,rail,road,popplace",
+                 transparent: "true", format: "image/png" });
+
+            map.addLayers([ol_wms, jpl_wms, dm_wms]);
+            map.addControl(new OpenLayers.Control.LayerSwitcher());
+            // map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+            map.zoomToMaxExtent();
+        }
+        // -->
+    </script>
+  </head>
+  <body onload="init()">
+    <div id="map"></div>
+  </body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/fullScreen.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/georss.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/georss.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/georss.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,42 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <style type="text/css">
+        #map {
+            width: 800px;
+            height: 400px;
+            border: 1px solid black;
+        }
+    </style>
+    <script src="../lib/OpenLayers.js"></script>
+    <script type="text/javascript">
+        <!--
+        var map, layer;
+
+        OpenLayers.ProxyHost = "/proxy/?url=";
+        function init(){
+            map = new OpenLayers.Map('map', {maxResolution:'auto'});
+            layer = new OpenLayers.Layer.WMS( "OpenLayers WMS", 
+                "http://labs.metacarta.com/wms/vmap0", {layers: 'basic'} );
+            map.addLayer(layer);
+            map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+            map.addControl(new OpenLayers.Control.LayerSwitcher());
+        }
+        function addUrl() {
+            var value = $('url').value;
+            var parts = value.split("/");
+                var newl = new OpenLayers.Layer.GeoRSS( parts[parts.length-1], value);
+                map.addLayer(newl);
+            $("url").value = "";
+        }
+        // -->
+    </script>
+  </head>
+  <body onload="init()">
+    <h1>GeoRSS in OpenLayers</h1>
+    <p style="font-size:.9em;">This demo uses the OpenLayers GeoRSS parser, which supports GeoRSS Simple and W3C GeoRSS. Only points are currently supported. <a href="http://trac.openlayers.org/wiki/HowToDownload">Get the code!</a></p>
+    <form onsubmit="return false;">
+   GeoRSS URL: <input type="text" id="url" size="50" /><input type="submit" onclick="addUrl(); return false;" value="Load Feed" onsubmit="addUrl(); return false;" />
+    </form>  
+    <div id="map"></div>
+  </body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/georss.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/georss.xml
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/georss.xml	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/georss.xml	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,378 @@
+<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet type="text/css" href="/css/rss.css" ?>
+
+<rdf:RDF  xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+  xmlns="http://purl.org/rss/1.0/"
+  xmlns:dc="http://purl.org/dc/elements/1.1/"
+  xmlns:georss="http://www.georss.org/georss">
+<docs>This is an RSS file.  Copy the URL into your aggregator of choice.  If you don't know what this means and want to learn more, please see: <span>http://platial.typepad.com/news/2006/04/really_simple_t.html</span> for more info.</docs><channel rdf:about="http://platial.com">
+<link>http://platial.com</link>
+<title>Crschmidt's Places At Platial</title>
+<description></description>
+<items>
+<rdf:Seq>
+<rdf:li resource="http://platial.com/place/90306"/>
+<rdf:li resource="http://platial.com/place/67230"/>
+<rdf:li resource="http://platial.com/place/65645"/>
+<rdf:li resource="http://platial.com/place/62200"/>
+<rdf:li resource="http://platial.com/place/28232"/>
+<rdf:li resource="http://platial.com/place/43666"/>
+<rdf:li resource="http://platial.com/place/28394"/>
+<rdf:li resource="http://platial.com/place/28251"/>
+<rdf:li resource="http://platial.com/place/28392"/>
+<rdf:li resource="http://platial.com/place/28391"/>
+<rdf:li resource="http://platial.com/place/28231"/>
+<rdf:li resource="http://platial.com/place/28393"/>
+<rdf:li resource="http://platial.com/place/31685"/>
+<rdf:li resource="http://platial.com/place/28596"/>
+<rdf:li resource="http://platial.com/place/28595"/>
+<rdf:li resource="http://platial.com/place/28594"/>
+<rdf:li resource="http://platial.com/place/28593"/>
+<rdf:li resource="http://platial.com/place/28592"/>
+<rdf:li resource="http://platial.com/place/28591"/>
+<rdf:li resource="http://platial.com/place/28590"/>
+<rdf:li resource="http://platial.com/place/28589"/>
+<rdf:li resource="http://platial.com/place/28588"/>
+<rdf:li resource="http://platial.com/place/28587"/>
+<rdf:li resource="http://platial.com/place/28586"/>
+<rdf:li resource="http://platial.com/place/28585"/>
+<rdf:li resource="http://platial.com/place/28584"/>
+<rdf:li resource="http://platial.com/place/28583"/>
+<rdf:li resource="http://platial.com/place/28582"/>
+<rdf:li resource="http://platial.com/place/28581"/>
+<rdf:li resource="http://platial.com/place/28580"/>
+<rdf:li resource="http://platial.com/place/28579"/>
+<rdf:li resource="http://platial.com/place/28578"/>
+<rdf:li resource="http://platial.com/place/28577"/>
+<rdf:li resource="http://platial.com/place/28576"/>
+<rdf:li resource="http://platial.com/place/28575"/>
+<rdf:li resource="http://platial.com/place/28574"/>
+<rdf:li resource="http://platial.com/place/28573"/>
+<rdf:li resource="http://platial.com/place/28572"/>
+<rdf:li resource="http://platial.com/place/28571"/>
+<rdf:li resource="http://platial.com/place/28570"/>
+</rdf:Seq>
+</items>
+</channel>
+<item rdf:about="http://platial.com/place/90306">
+<link>http://platial.com/place/90306</link>
+<title>Knitting Room</title>
+<description><![CDATA[This little shop is jammed full. Yarn, yarn everywhere. They make the most of every possible nook and cranny. I like this place also because they have a lot of different kinds of knitting needles in all different sizes. Also, the people who work here are younger and hipper than in the other stores I go to. I reccomend buying supplies here and then knitting your way through a good documentary at the Capitol Theater across the street.<br/>Address: 2 lake St, Arlington, MA <br/>Tags: knitting, yarn, pins and needles, handspun, hand dyed, novelty yarn, fancy, simple, young, hip, friendly, needles, addy, cute hats<br /><br /><a href="http://platial.com/place/90306">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/90306">Grab this on Platial</a> ]]></description>
+<georss:point>42.405696 -71.142197</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-06-08T17:35:01.942452+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/67230">
+<link>http://platial.com/place/67230</link>
+<title>Knitting Room</title>
+<description><![CDATA[This little shop is jammed full. Yarn, yarn everywhere. They make the most of every possible nook and cranny. I like this place also because they have a lot of different kinds of knitting needles in all different sizes. Also, the people who work here are younger and hipper than in the other stores I go to. I reccomend buying supplies here and then knitting your way through a good documentary at the Capitol Theater across the street.<br/>Address: 2 lake St, Arlington, MA <br/>Tags: knitting, yarn, pins and needles, handspun, hand dyed, novelty yarn, fancy, simple, young, hip, friendly, needles, addy, cute hats<br /><br /><a href="http://platial.com/place/67230">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/67230">Grab this on Platial</a> ]]></description>
+<georss:point>42.405524 -71.142273</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-04-24T11:35:26.733857+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/65645">
+<link>http://platial.com/place/65645</link>
+<title>†¢¢™£ˆøœ</title>
+<description><![CDATA[ijeª£∆µˆ˚î<br/>Address: 151 Erie St., Cambridge, MA<br/>Tags: platial graffiti<br /><br /><a href="http://platial.com/place/65645">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/65645">Grab this on Platial</a> ]]></description>
+<georss:point>42.352455 -71.110210</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-04-20T08:56:12.696224+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/62200">
+<link>http://platial.com/place/62200</link>
+<title>Allen Hall</title>
+<description><![CDATA[My dorm at UIUC.<br/>Address: 1301 W Gregory Dr, Urbana, IL<br/>Tags: dorm, uiuc, college<br/><a href="http://platial.com/place/62200"><img src="http://platial.comhttp://static.flickr.com/4/8576450_0d59cc2531_s.jpg"/></a><br/><br /><br /><a href="http://platial.com/place/62200">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/62200">Grab this on Platial</a> ]]></description>
+<georss:point>40.104172 -88.220623</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-04-14T08:01:01.872873+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28232">
+<link>http://platial.com/place/28232</link>
+<title>Bagby Hot Springs, OR</title>
+<description><![CDATA[Hot spring, temperature: 136  degress F, 58  degress C. However, the area around the springs are not exactly well looked upon by people who know the place.
+
+<br/>Tags: 20s, rosalie, romance, childhood, hike, camping, soak, relax, beautiful, hot springs, bathhouse, favorite, popular, crowded, organized, honeymoon tub, plumbing made from hollowed out trees, hot springs, mt hood, notorious car break in spot, rash, bacteria<br /><br /><a href="http://platial.com/place/28232">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28232">Grab this on Platial</a> ]]></description>
+<georss:point>44.936000 -122.173000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:10:18.553063+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/43666">
+<link>http://platial.com/place/43666</link>
+<title>Shooting Location for "The Field of Dreams" Film</title>
+<description><![CDATA[1989's Field of Dreams was a Best Picture Academy Award nominee, and the baseball field in the cornfield still stands today, and has become quite a tourist destination.  Games are occasionally played at the field, re-enacting professional baseball at the turn of the 20th Century.<br/>Address: Dyersville, Iowa<br/>Tags: iowa, baseball, movie locations, field of dreams, kevin costner, costner, dyersville, kinsella, james earl jones, chicago black sox, shoeless joe, joe jackson, famous farms, film, movie, cinema, shooting location<br /><br /><a href="http://platial.com/place/43666">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/43666">Grab this on Platial</a> ]]></description>
+<georss:point>42.481213 -91.111679</georss:point>
+<dc:creator>echinodermata</dc:creator>
+<dc:date>2006-03-23T11:40:17.654061+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28394">
+<link>http://platial.com/place/28394</link>
+<title>Moffetts (Bonneville) Hot Springs, WA</title>
+<description><![CDATA[Hot spring, temperature: 97  degress F, 36  degress C<br/>Tags: soak, hot springs, relax, nature<br /><br /><a href="http://platial.com/place/28394">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28394">Grab this on Platial</a> ]]></description>
+<georss:point>45.658000 -121.962000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:16:27.329816+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28251">
+<link>http://platial.com/place/28251</link>
+<title>Austin Hot Springs, OR</title>
+<description><![CDATA[Hot spring, temperature: 186  degress F, 86  degress C<br/>Tags: soak, hot springs, relax, nature, popular, crowded<br /><br /><a href="http://platial.com/place/28251">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28251">Grab this on Platial</a> ]]></description>
+<georss:point>45.021000 -122.009000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:11:04.489886+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28392">
+<link>http://platial.com/place/28392</link>
+<title>Rock Creek Hot Springs, WA</title>
+<description><![CDATA[Hot spring, temperature: Hot  degress F, Hot  degress C<br/>Tags: soak, hot springs, relax, nature<br /><br /><a href="http://platial.com/place/28392">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28392">Grab this on Platial</a> ]]></description>
+<georss:point>45.723000 -121.927000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:16:22.636855+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28391">
+<link>http://platial.com/place/28391</link>
+<title>St. Martins (Wind River) Hot Springs, WA</title>
+<description><![CDATA[Hot spring, temperature: 120  degress F, 49  degress C<br/>Tags: hot springs, soak, relax, nature, wonderful<br /><br /><a href="http://platial.com/place/28391">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28391">Grab this on Platial</a> ]]></description>
+<georss:point>45.728000 -121.800000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:16:20.383244+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28231">
+<link>http://platial.com/place/28231</link>
+<title>Breitenbush Hot Springs, OR</title>
+<description><![CDATA[Hot spring, temperature: 198  degress F, 92  degress C<br/>Tags: hot springs, resort, relax, nature, beautiful, http:www.breitenbush.com, soaking<br /><br /><a href="http://platial.com/place/28231">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28231">Grab this on Platial</a> ]]></description>
+<georss:point>44.782000 -121.975000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:10:16.529195+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28393">
+<link>http://platial.com/place/28393</link>
+<title>Collins Hot Springs, WA</title>
+<description><![CDATA[Hot spring, temperature: 122  degress F, 50  degress C<br/>Tags: portland, nice, hot springs, soak<br /><br /><a href="http://platial.com/place/28393">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28393">Grab this on Platial</a> ]]></description>
+<georss:point>45.701000 -121.728000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:16:24.648745+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/31685">
+<link>http://platial.com/place/31685</link>
+<title>Darwin's Ltd.</title>
+<description><![CDATA[Nice little coffee shop/cafe, free Wifi, close enough to walk from Harvard Square.<br/>Address: 148 Mount Auburn St, Cambridge, MA<br/>Tags: coffee, beer, sandwiches, freewifi<br/><a href="http://platial.com/place/31685"><img src="http://platial.comhttp://static.flickr.com/38/84885937_74fd3d1025_s.jpg"/></a><br/><br /><br /><a href="http://platial.com/place/31685">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/31685">Grab this on Platial</a> ]]></description>
+<georss:point>42.373974 -71.125053</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-10T09:24:08.152985+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28596">
+<link>http://platial.com/place/28596</link>
+<title>Huckleberry Hot Springs, WY</title>
+<description><![CDATA[Hot spring, temperature: Boiling  degress F, Boiling  degress C<br /><br /><a href="http://platial.com/place/28596">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28596">Grab this on Platial</a> ]]></description>
+<georss:point>44.115000 -110.684000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:24:32.283094+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28595">
+<link>http://platial.com/place/28595</link>
+<title>South Entrance Hot Springs, WY</title>
+<description><![CDATA[Hot spring, temperature: 156  degress F, 69  degress C<br/><a href="http://platial.com/place/28595"><img src="http://platial.comhttp://static.flickr.com/52/130989872_f1457f68b5_s.jpg"/></a><br/><br /><br /><a href="http://platial.com/place/28595">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28595">Grab this on Platial</a> ]]></description>
+<georss:point>44.142000 -110.656000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:24:30.279497+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28594">
+<link>http://platial.com/place/28594</link>
+<title>Crawfish Creek Hot Springs, WY</title>
+<description><![CDATA[Hot spring, temperature: 136  degress F, 58  degress C<br/><a href="http://platial.com/place/28594"><img src="http://platial.comhttp://static.flickr.com/52/128312256_d6a879924c_s.jpg"/></a><br/><br /><br /><a href="http://platial.com/place/28594">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28594">Grab this on Platial</a> ]]></description>
+<georss:point>44.157000 -110.699000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:24:28.280271+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28593">
+<link>http://platial.com/place/28593</link>
+<title>Crawfish Creek Hot Springs, WY</title>
+<description><![CDATA[Hot spring, temperature: 138  degress F, 59  degress C<br /><br /><a href="http://platial.com/place/28593">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28593">Grab this on Platial</a> ]]></description>
+<georss:point>44.165000 -110.723000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:24:20.364077+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28592">
+<link>http://platial.com/place/28592</link>
+<title>Snake Hot Springs, WY</title>
+<description><![CDATA[Hot spring, temperature: 136  degress F, 58  degress C<br /><br /><a href="http://platial.com/place/28592">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28592">Grab this on Platial</a> ]]></description>
+<georss:point>44.169000 -110.583000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:24:12.234974+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28591">
+<link>http://platial.com/place/28591</link>
+<title>Hot Springs, WY</title>
+<description><![CDATA[Hot spring, temperature: 142  degress F, 61  degress C<br /><br /><a href="http://platial.com/place/28591">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28591">Grab this on Platial</a> ]]></description>
+<georss:point>44.187000 -110.726000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:24:10.027857+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28590">
+<link>http://platial.com/place/28590</link>
+<title>Hot Springs on Upper Snake River, WY</title>
+<description><![CDATA[Hot spring, temperature: 167  degress F, 75  degress C<br /><br /><a href="http://platial.com/place/28590">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28590">Grab this on Platial</a> ]]></description>
+<georss:point>44.204000 -110.486000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:24:07.79658+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28589">
+<link>http://platial.com/place/28589</link>
+<title>Hot Springs on lewis Lake, WY</title>
+<description><![CDATA[Hot spring, temperature: 154  degress F, 68  degress C<br /><br /><a href="http://platial.com/place/28589">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28589">Grab this on Platial</a> ]]></description>
+<georss:point>44.276000 -110.636000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:24:05.683418+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28588">
+<link>http://platial.com/place/28588</link>
+<title>Rustic Geyser, WY</title>
+<description><![CDATA[Hot spring, temperature: 199  degress F, 93  degress C<br /><br /><a href="http://platial.com/place/28588">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28588">Grab this on Platial</a> ]]></description>
+<georss:point>44.282000 -110.506000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:24:03.66329+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28587">
+<link>http://platial.com/place/28587</link>
+<title>Bechler River Hot Springs, WY</title>
+<description><![CDATA[Hot spring, temperature: 194  degress F, 90  degress C<br /><br /><a href="http://platial.com/place/28587">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28587">Grab this on Platial</a> ]]></description>
+<georss:point>44.285000 -110.900000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:24:01.611442+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28586">
+<link>http://platial.com/place/28586</link>
+<title>Hot Springs, WY</title>
+<description><![CDATA[Hot spring, temperature: Boiling  degress F, 201  degress C<br /><br /><a href="http://platial.com/place/28586">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28586">Grab this on Platial</a> ]]></description>
+<georss:point>44.290000 -110.504000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:59.658699+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28585">
+<link>http://platial.com/place/28585</link>
+<title>Heart Lake Geyser Basin, WY</title>
+<description><![CDATA[Hot spring, temperature:  Middle Group  degress F, 174  degress C<br /><br /><a href="http://platial.com/place/28585">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28585">Grab this on Platial</a> ]]></description>
+<georss:point>44.299000 -110.517000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:57.181801+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28584">
+<link>http://platial.com/place/28584</link>
+<title>Hot Springs, WY</title>
+<description><![CDATA[Hot spring, temperature: Boiling  degress F, 201  degress C<br /><br /><a href="http://platial.com/place/28584">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28584">Grab this on Platial</a> ]]></description>
+<georss:point>44.307000 -110.526000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:55.240485+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28583">
+<link>http://platial.com/place/28583</link>
+<title>Hot Springs on lewis Lake, WY</title>
+<description><![CDATA[Hot spring, temperature: 199  degress F, 93  degress C<br /><br /><a href="http://platial.com/place/28583">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28583">Grab this on Platial</a> ]]></description>
+<georss:point>44.309000 -110.654000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:53.22295+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28582">
+<link>http://platial.com/place/28582</link>
+<title>Shoshone Geyser Basin, WY</title>
+<description><![CDATA[Hot spring, temperature: 203  degress F, 95  degress C<br /><br /><a href="http://platial.com/place/28582">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28582">Grab this on Platial</a> ]]></description>
+<georss:point>44.354000 -110.800000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:51.179049+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28581">
+<link>http://platial.com/place/28581</link>
+<title>Hot Springs on Continental Divide, WY</title>
+<description><![CDATA[Hot spring, temperature: 189  degress F, 87  degress C<br /><br /><a href="http://platial.com/place/28581">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28581">Grab this on Platial</a> ]]></description>
+<georss:point>44.401000 -110.936000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:49.077176+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28580">
+<link>http://platial.com/place/28580</link>
+<title>Hot Springs on Upper Firehole River, WY</title>
+<description><![CDATA[Hot spring, temperature: Hot  degress F, Hot  degress C<br /><br /><a href="http://platial.com/place/28580">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28580">Grab this on Platial</a> ]]></description>
+<georss:point>44.404000 -110.824000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:47.054664+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28579">
+<link>http://platial.com/place/28579</link>
+<title>Summit Lake Hot Springs, WY</title>
+<description><![CDATA[Hot spring, temperature: 162  degress F, 72  degress C<br /><br /><a href="http://platial.com/place/28579">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28579">Grab this on Platial</a> ]]></description>
+<georss:point>44.410000 -110.953000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:45.039394+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28578">
+<link>http://platial.com/place/28578</link>
+<title>Lone Star Geyser Basin, WY</title>
+<description><![CDATA[Hot spring, temperature:  Footbridge  degress F, 183  degress C<br /><br /><a href="http://platial.com/place/28578">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28578">Grab this on Platial</a> ]]></description>
+<georss:point>44.414000 -110.817000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:42.938808+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28577">
+<link>http://platial.com/place/28577</link>
+<title>West. Thumb Geyser Basin, WY</title>
+<description><![CDATA[Hot spring, temperature: 203  degress F, 95  degress C<br /><br /><a href="http://platial.com/place/28577">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28577">Grab this on Platial</a> ]]></description>
+<georss:point>44.417000 -110.570000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:40.90238+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28576">
+<link>http://platial.com/place/28576</link>
+<title>Lone Star Geyser, WY</title>
+<description><![CDATA[Hot spring, temperature: 199  degress F, 93  degress C<br /><br /><a href="http://platial.com/place/28576">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28576">Grab this on Platial</a> ]]></description>
+<georss:point>44.418000 -110.805000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:38.844625+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28575">
+<link>http://platial.com/place/28575</link>
+<title>Smoke Jumper Hot Springs, WY</title>
+<description><![CDATA[Hot spring, temperature: 198  degress F, 92  degress C<br /><br /><a href="http://platial.com/place/28575">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28575">Grab this on Platial</a> ]]></description>
+<georss:point>44.421000 -110.952000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:36.818513+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28574">
+<link>http://platial.com/place/28574</link>
+<title>West. Thumb Geyser Basin, WY</title>
+<description><![CDATA[Hot spring, temperature: 196  degress F, 91  degress C<br /><br /><a href="http://platial.com/place/28574">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28574">Grab this on Platial</a> ]]></description>
+<georss:point>44.422000 -110.574000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:34.767729+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28573">
+<link>http://platial.com/place/28573</link>
+<title>Potts Hot Spring Basin, WY</title>
+<description><![CDATA[Hot spring, temperature: 203  degress F, 95  degress C<br /><br /><a href="http://platial.com/place/28573">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28573">Grab this on Platial</a> ]]></description>
+<georss:point>44.433000 -110.581000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:32.749915+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28572">
+<link>http://platial.com/place/28572</link>
+<title>Hot Springs, WY</title>
+<description><![CDATA[Hot spring, temperature: Hot  degress F, Hot  degress C<br /><br /><a href="http://platial.com/place/28572">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28572">Grab this on Platial</a> ]]></description>
+<georss:point>44.433000 -110.813000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:30.829745+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28571">
+<link>http://platial.com/place/28571</link>
+<title>Hot Springs on Continental Divide, WY</title>
+<description><![CDATA[Hot spring, temperature: Hot  degress F, Hot  degress C<br /><br /><a href="http://platial.com/place/28571">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28571">Grab this on Platial</a> ]]></description>
+<georss:point>44.438000 -110.977000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:28.730401+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28570">
+<link>http://platial.com/place/28570</link>
+<title>SouthEastern Group, WY</title>
+<description><![CDATA[Hot spring, temperature: 198  degress F, 92  degress C<br /><br /><a href="http://platial.com/place/28570">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28570">Grab this on Platial</a> ]]></description>
+<georss:point>44.459000 -110.817000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:26.706763+00:00</dc:date>
+</item>
+</rdf:RDF>
\ No newline at end of file


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/georss.xml
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/getfeatureinfo.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/getfeatureinfo.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/getfeatureinfo.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,47 @@
+<html>
+<head>
+<script src="../lib/OpenLayers.js"></script>
+<style type="text/css">
+ul, li { padding-left: 0px; margin-left: 0px; }
+</style>
+<title>World Map Query</title> 
+</head>
+<body>
+  <a id='permalink' href="">Permalink</a><br />
+  <div style="float:right;width:28%">
+    <h1 style="font-size:1.3em;">CIA Factbook</h1>
+    <p style='font-size:.8em;'>Click a country to see statistics about the country below.</p>
+    <div id="nodeList">
+    </div>
+  </div>
+  <div id="map" style="width:70%; height:90%"></div>
+  <script defer="defer" type="text/javascript">
+    OpenLayers.ProxyHost = "/dev/examples/proxy.cgi?url=";
+    var map = new OpenLayers.Map('map', {'maxResolution':'auto'});
+    
+    var wms = new OpenLayers.Layer.WMS( "OpenLayers WMS", 
+        "http://world.freemap.in/cgi-bin/mapserv?map=/www/freemap.in/world/map/factbook.map", {'layers': 'factbook'} );
+    map.addLayer(wms);
+    map.addControl(new OpenLayers.Control.Permalink($('permalink')));
+    map.zoomToMaxExtent();
+    map.events.register('click', map, function (e) {
+            $('nodeList').innerHTML = "Loading... please wait...";
+            var url =  wms.getFullRequestString({
+                            REQUEST: "GetFeatureInfo",
+                            EXCEPTIONS: "application/vnd.ogc.se_xml",
+                            BBOX: wms.map.getExtent().toBBOX(),
+                            X: e.xy.x,
+                            Y: e.xy.y,
+                            INFO_FORMAT: 'text/html',
+                            QUERY_LAYERS: wms.params.LAYERS,
+                            WIDTH: wms.map.size.w,
+                            HEIGHT: wms.map.size.h});
+            OpenLayers.loadURL(url, '', this, setHTML);
+            Event.stop(e);
+      });
+    function setHTML(response) { 
+        $('nodeList').innerHTML = response.responseText;
+    }
+  </script>
+</body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/getfeatureinfo.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/google.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/google.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/google.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,83 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <style type="text/css">
+        #map {
+            width: 100%;
+            height: 512px;
+            border: 1px solid black;
+            background-color: red;
+        }
+    </style>
+
+    <!-- this gmaps key generated for http://openlayers.org/dev/ -->
+    <script src='http://maps.google.com/maps?file=api&amp;v=2&amp;key=ABQIAAAAjpkAC9ePGem0lIq5XcMiuhR_wWLPFku8Ix9i2SXYRVK3e45q1BQUd_beF8dtzKET_EteAjPdGDwqpQ'></script>
+    <!-- Localhost key -->
+    <!-- <script src='http://maps.google.com/maps?file=api&amp;v=2&amp;key=ABQIAAAAjpkAC9ePGem0lIq5XcMiuhT2yXp_ZAY8_ufC3CFXhHIE1NvwkxTS6gjckBmeABOGXIUiOiZObZESPg'></script>-->
+    <script src="../lib/OpenLayers.js"></script>
+    <script type="text/javascript">
+        <!--
+        var lon = 5;
+        var lat = 40;
+        var zoom = 17;
+        var map, layer;
+
+        function init(){
+            map = new OpenLayers.Map( $('map') , 
+            { controls: [new OpenLayers.Control.MouseDefaults()] , 'numZoomLevels':20});
+
+            var satellite = new OpenLayers.Layer.Google( "Google Satellite" , {type: G_SATELLITE_MAP, 'maxZoomLevel':18} );
+
+
+            map.addLayers([satellite]);
+            layer = new OpenLayers.Layer.WMS( "OpenLayers WMS", 
+                    "http://labs.metacarta.com/wms/vmap0", {layers: 'basic', 'transparent':true}, 
+                      {isBaseLayer: false} );
+            layer.setVisibility(false);
+            layer = new OpenLayers.Layer.WMS( "OpenLayers WMS", 
+                    "http://labs.metacarta.com/wms/vmap0", {layers: 'basic', 'transparent':true}, 
+                      {isBaseLayer: false} );
+            layer.setVisibility(false);
+            map.addLayer(layer);
+ var twms = new OpenLayers.Layer.WMS( "World Map", 
+         "http://world.freemap.in/cgi-bin/mapserv?", 
+                 {map: '/www/freemap.in/world/map/factbooktrans.map', transparent:'true',
+                         layers: 'factbook', 'format':'png'} );
+                         map.addLayer(twms);
+            markers = new OpenLayers.Layer.Markers("markers");
+            map.addLayer(markers);
+
+            map.setCenter(new OpenLayers.LonLat(10.205188,48.857593), 5);
+            map.addControl( new OpenLayers.Control.LayerSwitcher() );
+            map.addControl( new OpenLayers.Control.PanZoomBar() );
+
+        }
+        
+        function add() {
+
+            var url = 'http://boston.openguides.org/markers/AQUA.png';
+            var sz = new OpenLayers.Size(10, 17);
+            var calculateOffset = function(size) {
+                                return new OpenLayers.Pixel(-(size.w/2), -size.h);
+                             };           
+            var icon = new OpenLayers.Icon(url, sz, null, calculateOffset);
+            var barcelona = new OpenLayers.LonLat(2.13134765625,
+                                                  41.37062534198901);
+            marker = new OpenLayers.Marker(barcelona, icon);
+            markers.addMarker(marker);
+        }
+
+        function remove() {
+            markers.removeMarker(marker);
+        }
+                
+    // -->
+
+    </script>
+  </head>
+  <body onload="init()">
+    <h1>OpenLayers With Google Layer Example</h1>
+    <div id="map"></div>
+    <div style="background-color:green" onclick="add()"> click to add a marker to the map</div>
+    <div style="background-color:red" onclick="remove()"> click to remove the marker from the map</div>
+  </body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/google.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/image-layer.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/image-layer.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/image-layer.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,51 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <style type="text/css">
+        p {
+            width: 512px;
+        }
+        #map {
+            width: 512px;
+            height: 256px;
+            border: 1px solid gray;
+        }
+    </style>
+    <script src="../lib/OpenLayers.js"></script>
+    <script type="text/javascript">
+        <!--
+        function init(){
+            var map = new OpenLayers.Map('map');
+
+            var options = {maxResolution: 'auto', numZoomLevels: 3};
+            
+            var graphic = new OpenLayers.Layer.Image(
+                                'City Lights',
+                                'http://earthtrends.wri.org/images/maps/4_m_citylights_lg.gif',
+                                new OpenLayers.Bounds(-180, -88.759, 180, 88.759),
+                                new OpenLayers.Size(580, 288),
+                                options);
+
+            var jpl_wms = new OpenLayers.Layer.WMS( "NASA Global Mosaic",
+                "http://wms.jpl.nasa.gov/wms.cgi", 
+                {layers: "modis,global_mosaic"}, options);
+
+            map.addLayers([graphic, jpl_wms]);
+            map.addControl(new OpenLayers.Control.LayerSwitcher());
+            map.zoomToMaxExtent();
+        }
+        // -->
+    </script>
+  </head>
+  <body onload="init()">
+    <h1>OpenLayers Image Layer Example</h1>
+    <div id="map"></div>
+    <p>
+        The "City Lights" layer above is created from a single web accessible
+        image.  If you construct it without any resolution related options,
+        the layer will be given a single resolution based on the extent/size.
+        Otherwise, it behaves much like a regular layer.  This is primarily
+        intended to be used in an overview map - where another layer type
+        might not make a good overview.
+    </p>
+  </body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/image-layer.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/kamap.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/kamap.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/kamap.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,39 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <style type="text/css">
+        #map {
+            width: 800px;
+            height: 475px;
+            border: 1px solid black;
+        }
+    </style>
+    <script src="../lib/OpenLayers.js"></script>
+    <script type="text/javascript">
+        <!--
+        function init(){
+            var map = new OpenLayers.Map('map');
+
+            var ol_wms = new OpenLayers.Layer.WMS( "World Map", 
+                "http://labs.metacarta.com/wms/vmap0?",
+                 {layers: 'basic'} );
+
+            var jpl_wms = new OpenLayers.Layer.KaMap( "Satellite",
+                    "http://www.openlayers.org/world/index.php", {g: "satellite", map: "world"});
+            var dm_wms = new OpenLayers.Layer.WMS( "Canada",
+                "http://www2.dmsolutions.ca/cgi-bin/mswms_gmap",
+                {layers: "bathymetry,land_fn,park,drain_fn,drainage," +
+                         "prov_bound,fedlimit,rail,road,popplace",
+                 transparent: "true", format: "image/png" });
+
+            map.addLayers([ol_wms, jpl_wms, dm_wms]);
+            map.addControl(new OpenLayers.Control.LayerSwitcher());
+            map.zoomToMaxExtent();
+        }
+        // -->
+    </script>
+  </head>
+  <body onload="init()">
+    <h1>OpenLayers Example</h1>
+    <div id="map"></div>
+  </body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/kamap.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/kamap.txt
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/kamap.txt	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/kamap.txt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,508 @@
+<?php
+/*
+
+This is a PHP file to be used as a backend for a ka-Map layer. It requires
+PHP with Mapscript and libgd modules installed. The top of the file
+is a configuration section: please edit the variables in this configuration
+section to meet your needs, then rename this file to tile.php or something
+similar and put it in a web accessible directory. More information
+on the OpenLayers ka-Map layer is available from:
+
+  http://trac.openlayers.org/wiki/OpenLayers.Layer.KaMap
+
+*/
+/**********************************************************************
+ *
+ * $Id: tile.php,v 1.33 2006/02/07 03:19:55 pspencer Exp $
+ *
+ * purpose: a simple phpmapscript-based tile renderer that implements
+ *          rudimentary caching for reasonable efficiency.  Note the
+ *          cache never shrinks in this version so your disk could
+ *          easily fill up!
+ *
+ * author: Paul Spencer (pspencer at dmsolutions.ca)
+ *
+ * modifications by Daniel Morissette (dmorissette at dmsolutions.ca)
+ *
+ * Modified by Christopher Schmidt for OpenLayers redistribution.
+ * 
+ **********************************************************************
+ *
+ * Copyright (c) 2005, DM Solutions Group Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
+ * DEALINGS IN THE SOFTWARE.
+ *
+ **********************************************************************/
+ 
+
+/****************************************************************************** 
+ * basic system configuration
+ *
+ * kaMap! uses PHP/MapScript and the PHP GD extension to
+ * render tiles, and uses PHP/MapScript to generate initialization parameters
+ * a legend, and a keymap from the selected map file.
+ *
+ * Make sure to set the correct module names for your PHP extensions.
+ *
+ * WINDOWS USERS: you will likely need to use php_gd2.dll instead of php_gd.dll
+ */
+$szPHPMapScriptModule = 'php_mapscript.'.PHP_SHLIB_SUFFIX;
+$szPHPGDModule = 'php_gd.'.PHP_SHLIB_SUFFIX;
+
+/****************************************************************************** 
+ * tile generation parameters
+ *
+ * kaMap! generates tiles to load in the client application by first rendering
+ * larger areas from the map file and then slicing them up into smaller tiles.
+ * This approach reduces the overhead of loading PHP/MapScript and PHP GD and 
+ * drawing the map file.  These larger areas are referred to as metaTiles in
+ * the code.  You can set the size of both the small tiles and the metaTiles
+ * here.  A reasonable size for the small tiles seems to be 200 pixels square.
+ * Smaller tiles seem to cause problems in client browsers by causing too many
+ * images to be created and thus slowing performance of live dragging.  Larger
+ * tiles take longer to download to the client and are inefficient.
+ *
+ * The number of smaller tiles that form a metaTile can also be configured.
+ * This parameter allows tuning of the tile generator to ensure optimal
+ * performance and for label placement.  MapServer will produce labels only
+ * within a rendered area.  If the area is too small then features may be
+ * labelled multiple times.  If the area is too large, it may exceed MapServer,s
+ * maximum map size (by default 2000x2000) or be too resource-intensive on the
+ * server, ultimately reducing performance.
+ */
+$tileWidth = 256;
+$tileHeight = 256;
+$metaWidth = 5;
+$metaHeight = 5;
+/* $metaBuffer = Buffer size in pixels to add around metatiles to avoid 
+ * rendering issues along the edge of the map image
+ */
+$metaBuffer = 10;  
+    
+/****************************************************************************** 
+ * in-image debugging information - tile location, outlines etc.
+ * to use this, you need to remove images from your cache first.  This also
+ * affects the meta tiles - if debug is on, they are not deleted.
+ */
+$bDebug = false;
+ 
+/****************************************************************************** 
+ * aszMapFiles - an array of map files available to the application.  How this
+ * is used is determined by the application.  Each map file is entered into
+ * this array as a key->value pair.
+ *
+ * The key is the name to be used by the tile caching system to store cached
+ * tiles within the base cache directory.  This key should be a single word
+ * that uniquely identifies the map.
+ *
+ * The value associated with each key is an array of three values.  The first
+ * value is a human-readable name to be presented to the user (should the
+ * application choose to do so) and the second value is the path to the map
+ * file.  It is assumed that the map file is fully configured for use with
+ * MapServer/MapScript as no error checking or setting of values is done.  The 
+ * third value is an array of scale values for zooming.
+ */
+ 
+$aszMapFiles = array(
+  "world"   => array( "World", "/path/to/your/mapfile",
+               array( 10000  ), # in openlayers, the scale array doesn't matter.
+               "PNG24")
+
+/* Add more elements to this array to offer multiple mapfiles */
+
+);
+
+/****************************************************************************** 
+ * figure out which map file to use and set up the necessary variables for
+ * the rest of the code to use.  This does need to be done on every page load
+ * unfortunately.
+ *
+ * szMap should be set to the default map file to use but can change if
+ * this script is called with map=<mapname>. 
+ */
+$szMap = 'world';
+
+/****************************************************************************** 
+ * kaMap! caching
+ * 
+ * this is the directory within which kaMap! will create its tile cache.  The
+ * directory does NOT have to be web-accessible, but it must be writable by the
+ * web-server-user and allow creation of both directories AND files.
+ *
+ * the tile caching system will create a separate subdirectory within the base
+ * cache directory for each map file.  Within the cache directory for each map
+ * file, directories will be created for each group of layers.  Within the group
+ * directories, directories will be created at each of the configured scales
+ * for the application (see mapfile configuration above.)
+ */
+$szBaseCacheDir =  "/var/cache/kamap/";
+
+/*****  END OF CONFIGURABLE STUFF - unless you know what you are doing   *****/
+/*****                                                                   *****/
+/*****                                                                   *****/
+/*****                                                                   *****/
+/*****  END OF CONFIGURABLE STUFF - unless you know what you are doing   *****/
+
+if (isset($_REQUEST['map']) && isset($aszMapFiles[$_REQUEST['map']]))
+{
+    $szMap = $_REQUEST['map'];
+}
+
+$szMapCacheDir = $szBaseCacheDir.$szMap."/";
+$szMapName = $aszMapFiles[$szMap][0];
+$szMapFile = $aszMapFiles[$szMap][1];
+$anScales = $aszMapFiles[$szMap][2];
+setOutputFormat($aszMapFiles[$szMap][3]);
+/****************************************************************************** 
+ * output format of the map and resulting tiles
+ *
+ * The output format used with MapServer can greatly affect appearance and
+ * performance.  It is recommended to use an 8 bit format such as PNG
+ *
+ * NOTE: the tile caching code in tile.php is not configurable here.  It
+ * currently assumes that it is outputting 8bit PNG files.  If you change to
+ * PNG24 here then you will need to update tile.php to use the gd function
+ * imagecreatetruecolor.  If you change the output format to jpeg then
+ * you would need to change imagepng() to imagejpeg().  A nice enhancement
+ * would be to make that fully configurable from here.
+ */
+function setOutputFormat($szFormat)
+{
+    switch($szFormat) {
+        case "PNG24":
+            $GLOBALS['szMapImageFormat'] = 'PNG24'; //mapscript format name
+            $GLOBALS['szMapImageCreateFunction'] = "imagecreatefrompng"; // appropriate GD function
+            $GLOBALS['szImageExtension'] = '.png'; //file extension
+            $GLOBALS['szImageCreateFunction'] = "imagecreatetruecolor"; //or imagecreatetruecolor if PNG24 ...
+            $GLOBALS['szImageOutputFunction'] = "imagepng"; //or imagegif, imagejpeg ...
+            $GLOBALS['szImageHeader'] = 'image/png'; //the content-type of the image        
+            break;
+        case "GIF":
+            $GLOBALS['szMapImageFormat'] = 'GIF'; //mapscript format name
+            $GLOBALS['szMapImageCreateFunction'] = "imagecreatefromgif"; // appropriate GD function
+            $GLOBALS['szImageExtension'] = '.gif'; //file extension
+            $GLOBALS['szImageCreateFunction'] = "imagecreate"; //or imagecreatetruecolor if PNG24 ...
+            $GLOBALS['szImageOutputFunction'] = "imagegif"; //or imagegif, imagejpeg ...
+            $GLOBALS['szImageHeader'] = 'image/gif'; //the content-type of the image        
+            break;
+        case "JPEG":
+            $GLOBALS['szMapImageFormat'] = 'JPEG'; //mapscript format name
+            $GLOBALS['szMapImageCreateFunction'] = "imagecreatefromjpeg"; // appropriate GD function
+            $GLOBALS['szImageExtension'] = '.jpg'; //file extension
+            $GLOBALS['szImageCreateFunction'] = "imagecreatetruecolor"; //or imagecreatetruecolor if PNG24 ...
+            $GLOBALS['szImageOutputFunction'] = "imagejpeg"; //or imagegif, imagejpeg ...
+            $GLOBALS['szImageHeader'] = 'image/jpeg'; //the content-type of the image        
+            break;
+        case "PNG":
+            $GLOBALS['szMapImageFormat'] = 'PNG'; //mapscript format name
+            $GLOBALS['szMapImageCreateFunction'] = "imagecreatefrompng"; // appropriate GD function
+            $GLOBALS['szImageExtension'] = '.png'; //file extension
+            $GLOBALS['szImageCreateFunction'] = "imagecreate"; //or imagecreatetruecolor if PNG24 ...
+            $GLOBALS['szImageOutputFunction'] = "imagepng"; //or imagegif, imagejpeg ...
+            $GLOBALS['szImageHeader'] = 'image/png'; //the content-type of the image        
+            break;
+        case "DITHERED":
+	case "PNG8":
+            $GLOBALS['szMapImageFormat'] = 'dithered';
+            $GLOBALS['szMapImageCreateFunction'] = "imagecreatefrompng";
+            $GLOBALS['szImageExtension'] = '.png';
+            $GLOBALS['szImageCreateFunction'] = "imagecreate";
+            $GLOBALS['szImageOutputFunction'] = "imagepng";
+            $GLOBALS['szImageHeader'] = 'image/png';
+            break;
+    }
+}
+
+/**
+ * create all directories in a directory tree - found on the php web site
+ * under the mkdir function ...
+ */
+function makeDirs($strPath, $mode = 0775)
+{
+   return is_dir($strPath) or ( makeDirs(dirname($strPath), $mode) and mkdir($strPath, $mode) );
+}
+
+/**
+ * This function replaces all special characters in the given string.
+ *
+ * @param szString string - The string to convert.
+ *
+ * @return string converted
+ */
+function normalizeString($szString)
+{
+    // Normalize string by replacing all special characters
+    // e.g.    "http://my.host.com/cgi-bin/mywms?"
+    // becomes "http___my_host_com_cgi_bin_mywms_"
+    return preg_replace("/(\W)/", "_", $szString);
+}
+
+/* bug 1253 - root permissions required to delete cached files */
+$orig_umask = umask(0);
+
+/* create the main cache directory if necessary */
+if (!@is_dir($szMapCacheDir))
+    makeDirs($szMapCacheDir);
+
+/* get the various request parameters 
+ * also need to make sure inputs are clean, especially those used to
+ * build paths and filenames
+ */
+ /*
+ * the tile renderer accepts several parameters and returns a tile image from
+ * the cache, creating the tile only if necessary.
+ *
+ * all requests include the pixel location of the request at a certain scale
+ * and this script figures out the geographic location of the tile from the
+ * scale assuming that 0,0 in pixels is 0,0 in geographic units
+ * 
+ * Request parameters are:
+ *
+ * map: the name of the map to use.  This is handled by config.php.
+ * 
+ * t: top pixel position
+ * l: left pixel position
+ * s: scale
+ * g: (optional) comma-delimited list of group names to draw
+ * layers: (optional) comma-delimited list of layers to draw
+ * force: optional.  If set, force redraw of the meta tile.  This was added to
+ *        help with invalid images sometimes being generated.
+ * tileid: (optional) can be used instead of t+l to specify the tile coord.,
+ *         useful in regenerating the cache
+ */
+
+$top = isset( $_REQUEST['t'] ) ? intval($_REQUEST['t']) : 0;
+$left = isset( $_REQUEST['l'] ) ? intval($_REQUEST['l']) : 0;
+$scale = isset( $_REQUEST['s'] ) ? $_REQUEST['s'] : $anScales[0];
+$bForce = isset($_REQUEST['force'])? true : false;
+$groups = isset( $_REQUEST['g'] ) ? $_REQUEST['g'] : "";
+$layers = isset( $_REQUEST['layers'] ) ? $_REQUEST['layers'] : "";
+
+// dynamic imageformat ----------------------------------------------
+//use the function in config.php to set the output format
+if (isset($_REQUEST['i']))
+   setOutputFormat( $_REQUEST['i'] );
+//----------------------------------------------------------------
+
+/* tileid=t#####l#### can be used instead of t+l parameters. Useful in
+ * regenerating the cache for instance.
+ */
+if (isset( $_REQUEST['tileid']) &&
+    preg_match("/t(-?\d+)l(-?\d+)/", $_REQUEST['tileid'], $aMatch) )
+{
+    $top = intval($aMatch[1]);
+    $left = intval($aMatch[2]);
+}
+
+/* Calculate the metatile's top-left corner coordinates.
+ * Include the $metaBuffer around the metatile to account for various
+ * rendering issues happening around the edge of a map
+ */
+$metaLeft = floor( ($left)/($tileWidth*$metaWidth) ) * $tileWidth * $metaWidth;
+$metaTop = floor( ($top)/($tileHeight*$metaHeight) ) * $tileHeight *$metaHeight;
+$szMetaTileId = "t".$metaTop."l".$metaLeft;
+$metaLeft -= $metaBuffer;
+$metaTop -= $metaBuffer;
+
+/* caching is done by scale value, then groups and layers and finally metatile
+ * and tile id. Create a new directory if necessary
+ */
+$szGroupDir = $groups != "" ? normalizeString($groups) : "def"; 
+$szLayerDir = $layers != "" ? normalizeString($layers) : "def"; 
+
+$szCacheDir = $szMapCacheDir."/".$scale."/".$szGroupDir."/".$szLayerDir."/".$szMetaTileId;
+if (!@is_dir($szCacheDir))
+    makeDirs($szCacheDir);
+
+/* resolve cache hit - clear the os stat cache if necessary */
+$szTileId = "t".$top."l".$left;
+$szCacheFile = $szCacheDir."/".$szTileId.$szImageExtension;
+clearstatcache();
+
+$szMetaDir = $szCacheDir."/meta";
+if (!@is_Dir($szMetaDir))
+    makeDirs($szMetaDir);
+
+/* simple locking in case there are several requests for the same meta
+   tile at the same time - only draw it once to help with performance */
+$szLockFile = $szMetaDir."/lock_".$metaTop."_".$metaLeft;
+$fpLockFile = fopen($szLockFile, "a+");
+clearstatcache();
+if (!file_exists($szCacheFile) || $bForce)
+{
+    flock($fpLockFile, LOCK_EX);
+    fwrite($fpLockFile, ".");
+    
+    //check once more to see if the cache file was created while waiting for
+    //the lock
+    clearstatcache();
+    if (!file_exists($szCacheFile) || $bForce)
+    {
+        if (!extension_loaded('MapScript'))
+        {
+            dl( $szPHPMapScriptModule );
+        }
+        if (!extension_loaded('gd'))
+        {
+            dl( $szPHPGDModule);
+        }
+        
+        if (!@is_Dir($szMetaDir))
+            makeDirs($szMetaDir);    
+        
+        $oMap = ms_newMapObj($szMapFile);
+               
+        /* Metatile width/height include 2x the metaBuffer value */
+        $oMap->set('width', $tileWidth * $metaWidth + 2*$metaBuffer);
+        $oMap->set('height', $tileHeight * $metaHeight + 2*$metaBuffer);
+        
+        /* Tell MapServer to not render labels inside the metaBuffer area
+         * (new in 4.6) 
+         * TODO: Until MapServer bugs 1353/1355 are resolved, we need to
+         * pass a negative value for "labelcache_map_edge_buffer"
+         */
+        $oMap->setMetadata("labelcache_map_edge_buffer", -$metaBuffer);
+
+        $inchesPerUnit = array(1, 12, 63360.0, 39.3701, 39370.1, 4374754);
+        $geoWidth = $scale/($oMap->resolution*$inchesPerUnit[$oMap->units]);
+        $geoHeight = $scale/($oMap->resolution*$inchesPerUnit[$oMap->units]);
+        
+        /* draw the metatile */
+        $minx = $metaLeft * $geoWidth;
+        $maxx = $minx + $geoWidth * $oMap->width;
+        $maxy = -1 * $metaTop * $geoHeight;
+        $miny = $maxy - $geoHeight * $oMap->height;
+        
+        $nLayers = $oMap->numlayers;
+        $oMap->setExtent($minx,$miny,$maxx,$maxy);
+        $oMap->selectOutputFormat( $szMapImageFormat );               
+        $aszLayers = array();
+        if ($groups || $layers)
+        {
+            /* Draw only specified layers instead of default from mapfile*/
+            if ($layers)
+            {
+                $aszLayers = explode(",", $layers);
+            }
+
+            if ($groups)
+            {
+                $aszGroups = explode(",", $groups);
+            }
+
+            for($i=0;$i<$nLayers;$i++)
+            {
+                $oLayer = $oMap->getLayer($i);
+                if (($aszGroups && in_array($oLayer->group,$aszGroups)) ||
+                    ($aszLayers && in_array($oLayer->name,$aszLayers)) ||
+                    ($aszGroups && $oLayer->group == '' && 
+                     in_array( "__base__", $aszGroups)))
+                {
+                    $oLayer->set("status", MS_ON );
+                }
+                else
+                {
+                    $oLayer->set("status", MS_OFF );
+                }
+            }
+            //need transparency if groups or layers are used
+            $oMap->outputformat->set("transparent", MS_ON );
+        }
+        else
+        {
+            $oMap->outputformat->set("transparent", MS_OFF );
+        }
+
+        
+        $szMetaImg = $szMetaDir."/t".$metaTop."l".$metaLeft.$szImageExtension;
+        $oImg = $oMap->draw();
+        $oImg->saveImage($szMetaImg);
+        $oImg->free();
+        eval("\$oGDImg = ".$szMapImageCreateFunction."('".$szMetaImg."');");
+        if ($bDebug)
+        {
+            $blue = imagecolorallocate($oGDImg, 0, 0, 255);
+            imagerectangle($oGDImg, 0, 0, $tileWidth * $metaWidth - 1, $tileHeight * $metaHeight - 1, $blue );
+        }
+        for($i=0;$i<$metaWidth;$i++)
+        {
+            for ($j=0;$j<$metaHeight;$j++)
+            {
+                eval("\$oTile = ".$szImageCreateFunction."( ".$tileWidth.",".$tileHeight." );");
+                // Allocate BG color for the tile (in case the metatile has transparent BG)
+                $nTransparent = imagecolorallocate($oTile, $oMap->imagecolor->red, $oMap->imagecolor->green, $oMap->imagecolor->blue);
+                //if ($oMap->outputformat->transparent == MS_ON)
+                //{
+                    imagecolortransparent( $oTile,$nTransparent);
+                //}
+                $tileTop = $j*$tileHeight + $metaBuffer;
+                $tileLeft = $i*$tileWidth + $metaBuffer;
+                imagecopy( $oTile, $oGDImg, 0, 0, $tileLeft, $tileTop, $tileWidth, $tileHeight );
+                /* debugging stuff */
+                if ($bDebug)
+                {
+                    $black = imagecolorallocate($oTile, 1, 1, 1);
+                    $green = imagecolorallocate($oTile, 0, 128, 0 );
+                    $red = imagecolorallocate($oTile, 255, 0, 0);
+                    imagerectangle( $oTile, 1, 1, $tileWidth-2, $tileHeight-2, $green ); 
+                    imageline( $oTile, 0, $tileHeight/2, $tileWidth-1, $tileHeight/2, $red);
+                    imageline( $oTile, $tileWidth/2, 0, $tileWidth/2, $tileHeight-1, $red);
+                    imagestring ( $oTile, 3, 10, 10, ($metaLeft+$tileLeft)." x ".($metaTop+$tileTop), $black );
+                    imagestring ( $oTile, 3, 10, 30, ($minx+$i*$geoWidth)." x ".($maxy - $j*$geoHeight), $black );
+                }
+                $szTileImg = $szCacheDir."/t".($metaTop+$tileTop)."l".($metaLeft+$tileLeft).$szImageExtension;
+                eval("$szImageOutputFunction( \$oTile, '".$szTileImg."' );");
+                imagedestroy($oTile);
+                $oTile = null;
+            }
+        }
+        if ($oGDImg != null)
+        {   
+            imagedestroy($oGDImg);
+            $oGDImg = null;
+        }
+        if (!$bDebug)
+        {
+            unlink( $szMetaImg );
+        }
+    }
+    //release the exclusive lock
+    flock($fpLockFile, LOCK_UN );
+}
+
+//acquire shared lock for reading to prevent a problem that could occur
+//if a tile exists but is only partially generated.
+flock($fpLockFile, LOCK_SH);
+
+$h = fopen($szCacheFile, "r");
+header("Content-Type: ".$szImageHeader);
+header("Content-Length: " . filesize($szCacheFile));
+header("Expires: " . date( "D, d M Y H:i:s GMT", time() + 31536000 ));
+header("Cache-Control: max-age=31536000, must-revalidate" );
+fpassthru($h);
+fclose($h);
+
+//release lock
+fclose($fpLockFile);
+
+/* bug 1253 - root permissions required to delete cached files */
+umask($orig_umask);
+
+exit;
+?>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/kamap.txt
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/layer-opacity.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/layer-opacity.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/layer-opacity.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,86 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <style type="text/css">
+        body {
+            font-family: sans-serif;
+        }
+        #map {
+            width: 512px;
+            height: 350px;
+            border: 1px solid lightgray;
+        }
+        p {
+            width: 512px;
+        }
+        a {
+            text-decoration: none;
+            color: black;
+            font-weight: bold;
+            font-size: 1.1em;
+        }
+        #opacity {
+            padding: 0;
+            text-align: center;
+            width: 2em;
+            font-family: sans-serif;
+            background: transparent;
+            color: black;
+            border: 0;
+        }
+        p.note {
+            font-style: italic;
+            font-size: 0.8em;
+        }
+    </style>
+    <script src="../lib/OpenLayers.js"></script>
+    <script type="text/javascript">
+        <!--
+        var map = null;
+        var shade = null;
+        var maxOpacity = 0.9;
+        var minOpacity = 0.1;
+        function changeOpacity(byOpacity) {
+            var newOpacity = (parseFloat($('opacity').value) + byOpacity).toFixed(1);
+            newOpacity = Math.min(maxOpacity,
+                                  Math.max(minOpacity, newOpacity));
+            $('opacity').value = newOpacity;
+            shade.setOpacity(newOpacity);
+        }
+        function init(){
+            var options = {
+                projection: "EPSG:26912",
+                units: 'm',
+                maxExtent: new OpenLayers.Bounds(455402, 4967657, 473295, 4984095),
+                maxResolution: 'auto',
+                maxZoomLevel: 8
+            };
+            map = new OpenLayers.Map('map', options);
+            var drg = new OpenLayers.Layer.WMS("Topo Maps",
+                "http://terraservice.net/ogcmap.ashx",
+                {layers: "DRG"});
+            shade = new OpenLayers.Layer.WMS("Shaded Relief",
+                "http://ims.cr.usgs.gov:80/servlet19/com.esri.wms.Esrimap/USGS_EDC_Elev_NED_3", 
+                {layers: "HR-NED.IMAGE", reaspect: "false", transparent: 'true'},
+                {isBaseLayer: false, opacity: 0.3});
+            map.addLayers([drg, shade]);
+            map.addControl(new OpenLayers.Control.LayerSwitcher());
+            map.setCenter(new OpenLayers.LonLat(464348.5,4975876), 1);
+        }
+        // -->
+    </script>
+  </head>
+  <body onload="init()">
+    <h2>OpenLayers Layer Opacity Example</h2>
+    <div id="map"></div>
+    <p>
+        Note that if you also have the setOpacity method defined on the Layer
+        class, you can tweak the layer opacity after it has been added to the map.
+    </p>
+    <p>Opacity:
+        <a title="decrease opacity" href="javascript: changeOpacity(-0.1);">&lt;&lt;</a>
+        <input id="opacity" type="text" value="0.3" size="3" disabled="true" />
+        <a title="increase opacity" href="javascript: changeOpacity(0.1);">&gt;&gt;</a>
+    </p>
+    <p class="note">IE users: Wait until the shade layer has finished loading to try this.</p>
+  </body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/layer-opacity.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/lite.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/lite.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/lite.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,31 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <style type="text/css">
+        #map {
+            width: 800px;
+            height: 475px;
+            border: 1px solid black;
+        }
+    </style>
+    <script src="../lib/OpenLayers.js"></script>
+    <script type="text/javascript">
+        <!--
+        var lon = 5;
+        var lat = 40;
+        var zoom = 5;
+        var map, layer;
+
+        function init(){
+            map = new OpenLayers.Map( $('map') );
+            layer = new OpenLayers.Layer.WMS( "OpenLayers WMS", 
+                    "http://labs.metacarta.com/wms/vmap0", {layers: 'basic'} );
+            map.addLayer(layer);
+            map.setCenter(new OpenLayers.LonLat(lon, lat), zoom);
+        }
+        // -->
+    </script>
+  </head>
+  <body onload="init()">
+    <div id="map"></div>
+  </body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/lite.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/mapserver.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/mapserver.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/mapserver.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,34 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <style type="text/css">
+        #map {
+            width: 800px;
+            height: 475px;
+            border: 1px solid black;
+        }
+    </style>
+    <script src="../lib/OpenLayers.js"></script>
+    <script type="text/javascript">
+        <!--
+        var lon = 5;
+        var lat = 40;
+        var zoom = 5;
+        var map, layer;
+
+        function init(){
+            map = new OpenLayers.Map( $('map') );
+            layer = new OpenLayers.Layer.MapServer( "OpenLayers WMS", 
+                    "http://labs.metacarta.com/wms/vmap0", {layers: 'basic'} );
+            map.addLayer(layer);
+
+            map.setCenter(new OpenLayers.LonLat(lon, lat), zoom);
+            map.addControl( new OpenLayers.Control.LayerSwitcher() );
+        }
+        
+        // -->
+    </script>
+  </head>
+  <body onload="init()">
+    <div id="map"></div>
+  </body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/mapserver.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/markerResize.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/markerResize.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/markerResize.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,52 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <style type="text/css">
+        #map {
+            width: 512px;
+            height: 512px;
+            border: 1px solid black;
+        }
+    </style>
+    <script src="../lib/OpenLayers.js"></script>
+    <script type="text/javascript">
+        <!--
+        var map, layer;
+        var size, icon;
+        
+        function init(){
+            map = new OpenLayers.Map('map');
+            layer = new OpenLayers.Layer.WMS( "OpenLayers WMS", 
+                "http://labs.metacarta.com/wms/vmap0", {layers: 'basic'} );
+                
+            map.addLayer(layer);
+            var markers = new OpenLayers.Layer.Markers( "Markers" );
+            map.addLayer(markers);
+
+            size = new OpenLayers.Size(50,50);
+            calculateOffset = function(size) {
+                        return new OpenLayers.Pixel(-(size.w/2), -size.h); };
+            icon = new OpenLayers.Icon(
+                'http://boston.openguides.org/markers/AQUA.png',
+                size, null, calculateOffset);
+            markers.addMarker(
+                new OpenLayers.Marker(new OpenLayers.LonLat(-180,90), icon));
+
+            map.addControl(new OpenLayers.Control.LayerSwitcher());
+            map.zoomToMaxExtent();
+        }
+
+        function resize() {
+         
+            size = new OpenLayers.Size(size.w + 10, size.h + 10);
+            icon.setSize(size);   
+            
+        }
+        // -->
+    </script>
+  </head>
+  <body onload="init()">
+    <h1>OpenLayers Example</h1>
+    <div id="map"></div>
+    <div style="background-color:purple" onclick="resize()"> click to resize marker</div>
+  </body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/markerResize.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/markers.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/markers.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/markers.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,54 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <style type="text/css">
+        #map {
+            width: 512px;
+            height: 512px;
+            border: 1px solid black;
+        }
+    </style>
+    <script src="../lib/OpenLayers.js"></script>
+    <script type="text/javascript">
+        <!--
+        var map, layer;
+
+        function init(){
+            OpenLayers.ProxyHost="/proxy/?url=";
+            map = new OpenLayers.Map('map');
+            layer = new OpenLayers.Layer.WMS( "OpenLayers WMS", 
+                "http://labs.metacarta.com/wms/vmap0", {layers: 'basic'} );
+                
+            map.addLayer(layer);
+            map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+
+            var newl = new OpenLayers.Layer.Text( "text", { location:"./textfile.txt"} );
+            map.addLayer(newl);
+
+            var markers = new OpenLayers.Layer.Markers( "Markers" );
+            map.addLayer(markers);
+
+            var size = new OpenLayers.Size(10,17);
+            var offset = new OpenLayers.Pixel(-(size.w/2), -size.h);
+            var icon = new OpenLayers.Icon('http://boston.openguides.org/markers/AQUA.png',size,offset);
+            markers.addMarker(new OpenLayers.Marker(new OpenLayers.LonLat(0,0),icon));
+
+            var halfIcon = icon.clone();
+            markers.addMarker(new OpenLayers.Marker(new OpenLayers.LonLat(0,45),halfIcon));
+
+            marker = new OpenLayers.Marker(new OpenLayers.LonLat(90,10),icon.clone());
+            marker.setOpacity(0.2);
+            marker.events.register('mousedown', marker, function(evt) { alert(this.icon.url); Event.stop(evt); });
+            markers.addMarker(marker); 
+            map.addControl(new OpenLayers.Control.LayerSwitcher());
+            map.zoomToMaxExtent();
+
+            halfIcon.setOpacity(0.5);
+        }
+        // -->
+    </script>
+  </head>
+  <body onload="init()">
+    <h1>OpenLayers Example</h1>
+    <div id="map"></div>
+  </body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/markers.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/markersTextLayer.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/markersTextLayer.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/markersTextLayer.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,36 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <style type="text/css">
+        #map {
+            width: 512px;
+            height: 512px;
+            border: 1px solid black;
+        }
+    </style>
+    <script src="../lib/OpenLayers.js"></script>
+    <script type="text/javascript">
+        <!--
+        var map, layer;
+
+        function init(){
+            OpenLayers.ProxyHost="/proxy/?url=";
+            map = new OpenLayers.Map('map');
+            layer = new OpenLayers.Layer.WMS( "OpenLayers WMS", 
+                "http://labs.metacarta.com/wms/vmap0", {layers: 'basic'} );
+                
+            map.addLayer(layer);
+
+            var newl = new OpenLayers.Layer.Text( "text", {location: "./textfile.txt"} );
+            map.addLayer(newl);
+
+            map.addControl(new OpenLayers.Control.LayerSwitcher());
+            map.zoomToMaxExtent();
+        }
+        // -->
+    </script>
+  </head>
+  <body onload="init()">
+    <h1>OpenLayers Example</h1>
+    <div id="map"></div>
+  </body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/markersTextLayer.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/mm.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/mm.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/mm.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,57 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <style type="text/css">
+        #map {
+            width: 100%;
+            height: 512px;
+            border: 1px solid black;
+        }
+    </style>
+
+
+    <script type="text/javascript" src="http://clients.multimap.com/API/maps/1.1/metacarta_04"></script>
+
+
+    <script src="../lib/OpenLayers.js"></script>
+    <script type="text/javascript">
+        <!--
+ 
+        var lon = 5;
+        var lat = 40;
+        var zoom = 5;
+        var map, mmlayer, layer;
+
+        function init(){
+            map = new OpenLayers.Map( $('map') , 
+            {controls:[new OpenLayers.Control.MouseDefaults()]});
+
+            mmlayer = new OpenLayers.Layer.MultiMap( "MultiMap");
+            map.addLayer(mmlayer);
+            
+            markers = new OpenLayers.Layer.Markers("markers");
+            map.addLayer(markers);
+
+            map.setCenter(new OpenLayers.LonLat(lon, lat), zoom);
+            map.addControl( new OpenLayers.Control.LayerSwitcher() );
+            map.addControl( new OpenLayers.Control.PanZoomBar() );
+        }
+        
+        function add() {
+            
+            marker = new OpenLayers.Marker(new OpenLayers.LonLat(2, 41));
+            markers.addMarker(marker);
+        }
+
+        function remove() {
+            markers.removeMarker(marker);
+        }
+  // -->
+    </script>
+  </head>
+  <body onload="init()">
+    <h1>OpenLayers MultiMap Example</h1>
+    <div id="map"></div>
+    <div style="background-color:green" onclick="add()"> click to add the marker to the map</div>
+    <div style="background-color:red" onclick="remove()"> click to remove the marker from the map</div>
+  </body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/mm.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/mvs.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/mvs.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/mvs.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,130 @@
+<html>
+<!--
+     OpenLayers Map Viewer Service
+
+     Copyright 2005-2006 MetaCarta, Inc., released under the BSD License.
+-->
+<!-- 
+     This probably needs to be renamed index.html for deployment.
+     Specifically, it needs to be the default page for whatever
+     directory it is in.
+-->
+<head>
+
+    <!-- this gmaps key generated for http://openlayers.org/dev/ -->
+    <script src='http://maps.google.com/maps?file=api&amp;v=2&amp;key=ABQIAAAA9XNhd8q0UdwNC7YSO4YZghSPUCi5aRYVveCcVYxzezM4iaj_gxQ9t-UajFL70jfcpquH5l1IJ-Zyyw'></script>
+    <!-- Localhost key -->
+     <!--<script src='http://maps.google.com/maps?file=api&amp;v=2&amp;key=ABQIAAAAjpkAC9ePGem0lIq5XcMiuhT2yXp_ZAY8_ufC3CFXhHIE1NvwkxTS6gjckBmeABOGXIUiOiZObZESPg'></script>-->
+<script src="http://openlayers.org/dev/lib/OpenLayers.js"></script>
+
+<script>
+
+function runMVS() {
+    OpenLayers.ProxyHost = '/proxy/?url='; 
+    if (document.location.protocol != "file:") {
+        theArgs = OpenLayers.Util.getArgs();
+    } else {
+        theArgs = {};
+        theArgs.center = "0,0";
+        theArgs.zoom = "0";            
+        theArgs.data = "textfile.txt";
+        theArgs.controls = 'panzoom,mouse';
+        theArgs.layers = 'openlayers';
+    }
+
+
+    // ----
+    // TODO: Handle all this parsing better.
+    var safeArgs = {}
+
+    var DEFAULT_LAT = 0;
+    var DEFAULT_LON = 0;
+    var DEFAULT_ZOOM_LEVEL = 0;
+    var DEFAULT_CONTROLS = ['panzoom','mouse'];
+    var DEFAULT_LAYERS = ['openlayers'];
+    
+    var IDX_LAT = 0;
+    var IDX_LON = 1;
+
+    safeArgs.centerLat = theArgs.center ? 
+        parseFloat(theArgs.center.split(",")[IDX_LAT]) : DEFAULT_LAT;
+    
+    safeArgs.centerLon = theArgs.center ? 
+        parseFloat(theArgs.center.split(",")[IDX_LON]) : DEFAULT_LON;
+
+    safeArgs.zoom = theArgs.zoom ? parseInt(theArgs.zoom) : DEFAULT_ZOOM_LEVEL;
+    
+    safeArgs.controls = theArgs.controls ? 
+        theArgs.controls.split(",") : DEFAULT_CONTROLS;
+    
+    safeArgs.layers = theArgs.layers ? 
+        theArgs.layers.split(",") : DEFAULT_LAYERS;
+    
+    safeArgs.data = theArgs.data; // TODO: Make this "safe".
+    safeArgs.marker = theArgs.marker; // TODO: Make this "safe".
+    
+    // -----
+    var theMVS = new OpenLayers.Map($('map'), {controls: [], maxResolution: 'auto'});
+    for(var i = 0; i < safeArgs.controls.length; i++) {
+        switch(safeArgs.controls[i]) {
+            case 'panzoombar':
+                theMVS.addControl(new OpenLayers.Control.PanZoomBar());
+                break;
+            case 'panzoom':
+                theMVS.addControl(new OpenLayers.Control.PanZoom());
+                break;
+            case 'layerswitcher':
+                theMVS.addControl(new OpenLayers.Control.LayerSwitcher());
+                break;
+            case 'mouse':
+                theMVS.addControl(new OpenLayers.Control.MouseDefaults());
+                break;
+        }
+    }
+    for(var i = 0; i < safeArgs.layers.length; i++) {
+        switch(safeArgs.layers[i]) {
+            case 'openlayers':
+                theMVS.addLayer(
+                  new OpenLayers.Layer.WMS( "OpenLayers WMS", 
+                        "http://labs.metacarta.com/wms/vmap0", 
+                        {layers: 'basic'} )
+                  );
+                break;
+            case 'nasa': 
+                theMVS.addLayer(
+                    new OpenLayers.Layer.WMS("NASA Mosaic", 
+                       "http://wms.jpl.nasa.gov/wms.cgi",
+                       {"EXCEPTIONS" : "application/vnd.ogc.se_inimage",
+                        "format" : "image/jpeg",
+                        layers:"modis,global_mosaic"}
+                       ));
+                break;
+            case 'gmaps':
+                theMVS.addLayer(
+                    new OpenLayers.Layer.Google( "Google" , {type: G_HYBRID_MAP })
+                );
+                break;
+        }
+    }
+    theMVS.setCenter(new OpenLayers.LonLat(safeArgs.centerLon, safeArgs.centerLat), safeArgs.zoom);
+
+    if (safeArgs.marker) {
+        var m = new OpenLayers.Layer.Markers("Marker");
+        m.addMarker(new OpenLayers.Marker(new OpenLayers.LonLat(safeArgs.centerLon, safeArgs.centerLat)));
+        theMVS.addLayer(m);
+    }
+    if (safeArgs.data) {
+        theMVS.addLayer(new OpenLayers.Layer.Text("Data", {location: safeArgs.data}));
+    }
+
+}
+</script>
+</head>
+<body style="margin:0px;"
+      onload="runMVS();">
+<div id="map"
+     style="width: 100%; height: 100%;
+            background: lightyellow;
+           "></div>
+</body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/mvs.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/notile.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/notile.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/notile.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,30 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <style type="text/css">
+        #map {
+            width: 512px;
+            height: 512px;
+            border: 1px solid black;
+        }
+    </style>
+    <script src="../lib/OpenLayers.js"></script>
+    <script type="text/javascript">
+        <!--
+        function init(){
+            var map = new OpenLayers.Map('map', {maxResolution: 'auto'});
+
+            var ol_wms = new OpenLayers.Layer.WMS.Untiled( "OpenLayers WMS", 
+                "http://labs.metacarta.com/wms/vmap0?", {layers: 'basic'} );
+            ol_wms.addOptions({isBaseLayer: true});
+            map.addLayers([ol_wms]);
+            map.addControl(new OpenLayers.Control.LayerSwitcher());
+            map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+        }
+        // -->
+    </script>
+  </head>
+  <body onload="init()">
+    <h1>OpenLayers Example</h1>
+    <div id="map"></div>
+  </body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/notile.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/openstreetmap.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/openstreetmap.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/openstreetmap.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,35 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <style type="text/css">
+        #map {
+            width: 800px;
+            height: 475px;
+            border: 1px solid black;
+        }
+    </style>
+    <script src="../lib/OpenLayers.js"></script>
+    <script type="text/javascript">
+        <!--
+        var zoom = 5;
+        var map, layer;
+
+        function init(){
+            map = new OpenLayers.Map( $('map') );
+            layer = new OpenLayers.Layer.WMS( "OpenStreetMap WMS", 
+                    "http://tile.openstreetmap.org/ruby/wmsmod.rbx?" );
+            layer.setTileSize(new OpenLayers.Size(256,128));
+            map.addLayer(layer);
+
+            map.setCenter(new OpenLayers.LonLat(-0.02197265625,51.492919921875), 13);
+            map.addControl( new OpenLayers.Control.LayerSwitcher() );
+        }
+        
+        
+
+        // -->
+    </script>
+  </head>
+  <body onload="init()">
+    <div id="map"></div>
+  </body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/openstreetmap.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/popups.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/popups.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/popups.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,124 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <style type="text/css">
+        #map {
+            width: 512px;
+            height: 512px;
+            border: 1px solid black;
+        }
+    </style>
+    <script src="../lib/OpenLayers.js"></script>
+    <script type="text/javascript">
+        <!--
+        var map, layer, popup;
+        var markers, feature, marker;
+        
+        function init(){
+            map = new OpenLayers.Map('map');
+            layer = new OpenLayers.Layer.WMS( "OpenLayers WMS", 
+                "http://labs.metacarta.com/wms/vmap0", {layers: 'basic'} );
+            map.addLayer(layer);
+
+            map.addControl(new OpenLayers.Control.LayerSwitcher());
+            map.zoomToMaxExtent();
+            
+        }
+
+        function changer() {
+            popup.setBackgroundColor("red");
+            popup.setSize(new OpenLayers.Size(100,600));
+//            popup.moveTo(new OpenLayers.Pixel(120,120));
+//            popup.setOpacity(.5);
+            popup.setBorder("2px solid");    
+            popup.setContentHTML("High Chickens");            
+        }
+
+        function add() {
+            popup = new OpenLayers.Popup("chicken", 
+                                         new OpenLayers.LonLat(5,40),
+                                         new OpenLayers.Size(200,200),
+                                         "example popup",
+                                         true);
+        
+            map.addPopup(popup);
+        }        
+
+        function addAnchor() {
+            popup = new OpenLayers.Popup.Anchored("chicken", 
+                                         new OpenLayers.LonLat(5,40),
+                                         new OpenLayers.Size(200,200),
+                                         "example popup");
+        
+            map.addPopup(popup);
+        }        
+        
+        function addMarker() {
+    
+            markers = new OpenLayers.Layer.Markers("zibo");
+            map.addLayer(markers);
+
+            feature = new OpenLayers.Feature(layer, 
+                                                 new OpenLayers.LonLat(0,0));
+
+
+            marker = feature.createMarker();
+            
+            markers.addMarker(marker);
+            marker.events.register("mousedown", marker, mousedown);
+                                                             
+        }
+        
+        function mousedown(evt) {
+            if (popup == null) {
+                popup = feature.createPopup();
+                popup.setContentHTML("<a href='http://www.somethingconstructive.net' target='_blank'>click me</a>");
+                popup.setBackgroundColor("yellow");
+                popup.setOpacity(0.7);
+                markers.map.addPopup(popup);
+            } else {
+                markers.map.removePopup(popup);
+                popup.destroy();
+                popup = null;
+            }
+            Event.stop(evt);
+        }        
+
+        /**
+         * @param {Event} evt
+         */
+        function onPopupMouseDown(evt) {
+            markers.map.removePopup(popup);
+            popup.destroy();
+            popup = null;
+            Event.stop(evt);
+        }
+
+
+        function destroy() {
+            popup.destroy();
+        }
+
+        function remove() {
+            markers.removeMarker(marker);
+        }
+
+        function removelayer() {
+            layer.destroy();
+//            map.removeLayer(markers);
+        }
+
+
+        // -->
+    </script>
+  </head>
+  <body onload="init()">
+    <div id="map"></div>
+    <div style="background-color:purple" onclick="add()"> click to add Popup to map</div>
+    <div style="background-color:green" onclick="addMarker()"> click to add a Marker with an AnchoredBubble popup</div>
+    <div style="background-color:blue" onclick="changer()"> click to modify popup's attributes</div>
+    <div style="background-color:red" onclick="remove()"> click to remove the popup from map</div>
+    <div style="background-color:grey" onclick="removelayer()"> click to remove the markers layer</div>
+    <div style="background-color:orange" onclick="alert(marker.onScreen())"> marker.onscreen()?</div>
+    <div style="background-color:yellow" onclick="destroy()"> click to destroy the popup from map</div> 
+  </body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/popups.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/proxy.cgi
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/proxy.cgi	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/proxy.cgi	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,54 @@
+#!/usr/bin/env python
+
+
+"""This is a blind proxy that we use to get around browser
+restrictions that prevent the Javascript from loading pages not on the
+same server as the Javascript.  This has several problems: it's less
+efficient, it might break some sites, and it's a security risk because
+people can use this proxy to browse the web and possibly do bad stuff
+with it.  If you can get your code signed (see:
+http://trac.openlayers.org/wiki/HowToSignJavascript), then you should
+modify Parameters.js so that this isn't used.  Otherwise, you're stuck
+with it.  It only loads pages via http and https, but it can load any
+content type. XML and HTML are both currently used by Openlayers."""
+
+import urllib
+import cgi
+
+fs = cgi.FieldStorage()
+url = fs.getvalue('url', "http://openlayers.org")
+
+# Designed to prevent Open Proxy type stuff.
+
+allowedHosts = ['www.openlayers.org', 'openlayers.org', 'octo.metacarta.com', 'merrimack.metacarta.com', 'labs.metacarta.com', 'world.freemap.in']
+
+try:
+    host = url.split("/")[2]
+    if allowedHosts and not host in allowedHosts:
+        print "Status: 502 Bad Gateway"
+        print "Content-Type: text/plain"
+        print
+        print "This proxy does not allow you to access that location."
+    
+    elif url.startswith("http://") or url.startswith("https://"):
+    
+        y = urllib.urlopen(url)
+        
+        headers = str(y.info()).split('\n')
+        for h in headers:
+            if h.startswith("Content-Type:"):
+                print h
+        print
+        
+        print y.read()
+        
+        y.close()
+    else:
+        print """Content-Type: text/plain
+ 
+Illegal request."""
+except Exception, E:
+    print "Status: 500 Unexpected Error"
+    print "Content-Type: text/plain"
+    print 
+    print "Some unexpected error occurred. Error text was:", E


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/proxy.cgi
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/scroll.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/scroll.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/scroll.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,151 @@
+
+<!DOCTYPE html
+    PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+<head>
+  <title>OpenLayers: Home</title>
+  <link rel="stylesheet" href="http://openlayers.org/common.css" type="text/css" />
+  <link rel="stylesheet" href="http://trac.openlayers.org/chrome/common/css/trac.css" type="text/css" />
+  <link rel="stylesheet" href="http://trac.openlayers.org/chrome/common/css/wiki.css" type="text/css" />
+  <link rel="stylesheet" href="http://trac.openlayers.org/chrome/common/css/tracnav.css" type="text/css" />
+  <link rel="stylesheet" href="http://openlayers.org/website.css" type="text/css" />
+  <script type="text/javascript" src="//trac.openlayers.org/chrome/common/js/trac.js"></script>
+  <!--[if lt IE 7]>
+  <script defer="defer" type="text/javascript" src="/pngfix.js"></script>
+  <![endif]-->
+</head>
+<body>
+
+<div id="olbanner">
+<img class="openlayersbannerimg" src="http://www.openlayers.org/images/OpenLayers.trac.png" style="height: 44px; width: 49px" border="0" alt="OpenLayers" />
+<a class="openlayersbanner" href="http://www.openlayers.org/" >
+OpenLayers</a>
+</div>
+<div id="navcontainer">
+<a class="navtabtraccurrent" href="http://www.openlayers.org">Home</a>
+<a class="navtab" href="http://trac.openlayers.org">Support &amp; Development</a>
+</div>
+
+
+<div id="mainnav" class="nav"><ul><li class="first"><a href="http://trac.openlayers.org/wiki" accesskey="1">Wiki</a></li><li><a href="/QuickTutorial" accesskey="2">Tutorial</a></li><li><a href="http://trac.openlayers.org/wiki/HowToDownload" accesskey="3">Download</a></li><li><a href="/gallery">Gallery</a></li><li><a href="/EmailLists">Email Lists</a></li></ul></div>
+
+
+  <div id="main">
+    <div id="mapFrame">
+      <div id="map"></div>
+      <div align="center"><b>Put an open map widget in any web page!</b></div>
+      <div align="center">
+        Double-click to zoom in, and drag to pan. Hold down the shift key
+        and drag to zoom to a particular region.
+      </div>
+    </div>
+    <h3>Get OpenLayers Now</h3>
+    <p>
+    Latest stable release: <a href="/api/2/OpenLayers.js">Link to the hosted version</a> | <a href="/download/OpenLayers-2.0.tar.gz">OpenLayers-2.0.tar.gz</a> | <a href="/doc/reference.html">Class Documentation</a> |  <a href="/gallery/">See Screenshots</a> 
+    </p>
+    <p><b>Latest Development Release</b>: Test out OpenLayers 2.1-RC2! <a href="/api/2.1-rc2/OpenLayers.js">Link to 2.1-RC2 Hosted Version</a>.</p>   
+   <p><b>FOSS4G</b>: Was the FOSS4G presentation too early for you this morning? The presentation is <a href="http://openlayers.org/presentations/foss4g2006/openlayers.odp">available in Open Office Impress format</a>, and the examples are <a href="http://openlayers.org/presentations/foss4g2006/examples/">available as well</a>.</p>  
+    <h3>About...</h3>
+    <p>OpenLayers makes it easy to put a dynamic map in any web page.  It can
+display map tiles and markers loaded from any source.    <a
+href="http://www.metacarta.com/">MetaCarta</a> developed the initial version of
+OpenLayers and gave it to the public to further the use of geographic
+information of all kinds.  OpenLayers is 
+completely free, Open Source JavaScript, released under the BSD License.</p>
+
+    <h3>Put a map in your page.</h3>
+    <p>We've released a Map Viewer Service that lets you put a map in your page easily.  Just put this HTML in your page:</p>
+    <pre>
+      &lt;iframe src="http://openlayers.org/viewer/"
+        width="400px" height="200px"
+        scrolling="no"
+        marginwidth="0" marginheight="0"
+        frameborder="0"&gt;
+      &lt;/iframe&gt;</pre>
+
+<p>Read more examples in the <a href="QuickTutorial">20-second tutorial</a>.  The visual appearance of the MapViewer is not yet stable.  If you like it, please join the <a href="EmailLists">users' email list</a> and tell us about your use.</p>
+
+    <h3>For Developers!</h3>
+<p>OpenLayers is a pure JavaScript library for displaying map data in most
+modern web browsers, with no server-side dependencies. OpenLayers implements a
+(still-developing) <a href="//trac.openlayers.org/wiki/Documentation">JavaScript
+API</a> for building rich web-based geographic applications, similar to the
+Google Maps and MSN Virtual Earth APIs, with one important difference --
+OpenLayers is Free Software, developed for and by the Open Source software
+community.</p>
+
+<p>Furthermore, OpenLayers implements industry-standard methods for geographic
+data access, such as the OpenGIS Consortium's Web Mapping Service (WMS) and Web
+Feature Service (WFS) protocols. Under the hood, OpenLayers is written in
+object-oriented JavaScript, using <a
+href="//prototype.conio.net/">Prototype.js</a> and components from the <a
+href="//openrico.org/">Rico<a> library. The OpenLayers code base already has
+hundreds of <a href="/dev/tests/run-tests.html">unit tests</a>, via the <a
+href="http://straytree.com/TestAnotherWay/doc/index.html">Test.AnotherWay</a> 
+framework.</p>
+
+<p>As a framework, OpenLayers is intended to separate map <i>tools</i> from map
+<i>data</i> so that all the tools can operate on all the data sources.  This
+separation breaks the proprietary silos that earlier GIS revolutions have
+taught civilization to avoid.  The mapping revolution on the public Web should
+benefit from the experience of history.</p>
+
+    <h3>Getting the Code</h3>
+
+<p>Releases are made available on the <a href="/download/">downloads</a> page.
+Additionally, if you wish to use OpenLayers in a web application, you can 
+include 
+  <tt>
+    <a href="http://www.openlayers.org/api/OpenLayers.js">http://www.openlayers.org/api/OpenLayers.js</a>
+   </tt> in your page, to always get the latest release.</p>
+
+<p> The code is also available in our
+<a href="//trac.openlayers.org/wiki/HowToDownload">Subversion repository</a>.
+Using Subversion, you can keep up to the absolute bleeding edge of the code.
+If you wish to report a bug in the API, and you are able to use Subversion, 
+please see if the bug has been fixed in Subversion first: OpenLayers is
+under rapid development, so things change quickly.
+</p>
+
+<p>If you don't have Subversion or don't want to download the code, you can
+still try some <a href="/dev/examples/">live examples</a> on
+openlayers.org. If you're familiar with JavaScript, try viewing the source
+of the examples to get an idea how the OpenLayers library is used.</p>
+
+<p>OpenLayers is still undergoing rapid development, so expect a lot to change
+in the next few weeks and months. We need your support! Please check the <a
+href="//trac.openlayers.org">wiki</a> for the very latest updates and
+documentation, and thank you for taking an interest.</p> 
+
+  </div>
+  <script type="text/javascript" src="../lib/OpenLayers.js"></script>
+  <script type="text/javascript">
+        <!--
+            var map = new OpenLayers.Map('map');
+
+            var ol_wms = new OpenLayers.Layer.WMS( "World Map", 
+                "http://labs.metacarta.com/wms/vmap0?",
+                 {layers: 'basic'} );
+
+            var jpl_wms = new OpenLayers.Layer.KaMap( "Satellite",
+                    "/world/index.php", {g: "satellite", map: "world"});
+            var dm_wms = new OpenLayers.Layer.WMS( "Canada",
+                "http://www2.dmsolutions.ca/cgi-bin/mswms_gmap",
+                {layers: "bathymetry,land_fn,park,drain_fn,drainage," +
+                         "prov_bound,fedlimit,rail,road,popplace",
+                 transparent: "true", format: "image/png" });
+
+            dm_wms.setVisibility(false);
+
+            map.addLayers([ol_wms, jpl_wms, dm_wms]);
+            map.addControl(new OpenLayers.Control.LayerSwitcher());
+            map.zoomToMaxExtent();
+        // -->
+    </script>
+  <div id="sidebar"></div>
+  <div id="footer"></div>
+</div>
+  </body>
+</html>
+


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/scroll.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/setextent.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/setextent.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/setextent.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,29 @@
+<html>
+<head>
+<title>Setting a visual Extent</title>
+<script src="../lib/OpenLayers.js"></script>
+</head>
+<body>
+<h1>Setting a Visual Extent</h1>
+<p>
+  Because the ability to set the map to a given extent is limited by the 
+  current resolutions available, zoomToExtent will not always set the map to 
+  exactly the right extent. In order to visually annotate the actual extent, 
+  this example, will use the Boxes layer to visually describe the desired
+  extent as well as setting the map extent.
+</p>
+  <div style="width:100%; height:75%" id="map"></div>
+  <script defer="defer" type="text/javascript">
+    var map = new OpenLayers.Map('map');
+    var bounds = new OpenLayers.Bounds(-45,-45, 0, 45); 
+    var wms = new OpenLayers.Layer.WMS( "OpenLayers WMS", 
+        "http://labs.metacarta.com/wms/vmap0", {layers: 'basic'} );
+    map.addLayer(wms);
+    map.zoomToExtent(bounds);
+    var boxes = new OpenLayers.Layer.Boxes("boxes");
+    var box = new OpenLayers.Marker.Box(bounds);
+    boxes.addMarker(box);
+    map.addLayer(boxes);
+  </script>
+</body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/setextent.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/textfile.txt
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/textfile.txt	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/textfile.txt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,4 @@
+point	title	description	iconSize	
+10,20	my orange title	my orange description	21,25
+2,4	my aqua title	my aqua description	21,25
+42,-71	my purple title	my purple description<br/>is great.	21,25


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/textfile.txt
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/tiger.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/tiger.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/tiger.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,36 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <style type="text/css">
+        #map {
+            width: 700px;
+            height: 512px;
+            border: 1px solid black;
+        }
+    </style>
+    <script src="../lib/OpenLayers.js"></script>
+    <script type="text/javascript">
+        <!--
+        function init(){
+            var map = new OpenLayers.Map('map', {'maxResolution': 1.40625/4, controls:[]});
+
+            var ka_wms = new OpenLayers.Layer.KaMap( "TIGER (KaMap)", 
+                "http://boston.freemap.in/tile.php" , { map: 'tiger', layers: 'TIGER' });
+            var jpl_wms = new OpenLayers.Layer.WMS( "NASA Global Mosaic",
+                "http://wms.jpl.nasa.gov/wms.cgi", 
+                {layers: "modis,global_mosaic"});
+            jpl_wms.setVisibility(false);
+            ka_wms.addOptions({isBaseLayer: false});
+            map.addLayers([jpl_wms,ka_wms]); 
+            map.addControl(new OpenLayers.Control.LayerSwitcher());
+            map.addControl(new OpenLayers.Control.MouseToolbar());
+            map.addControl(new OpenLayers.Control.PanZoomBar());
+            map.setCenter(new OpenLayers.LonLat(-96, 35), 2);
+        }
+        // -->
+    </script>
+  </head>
+  <body onload="init()">
+    <h1>OpenLayers Example</h1>
+    <div id="map"></div>
+  </body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/tiger.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/urban.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/urban.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/urban.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,36 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <style type="text/css">
+        #map {
+            width: 800px;
+            height: 475px;
+            border: 1px solid black;
+        }
+    </style>
+    <script src="../lib/OpenLayers.js"></script>
+    <script type="text/javascript">
+        <!--
+        function init(){
+            var mapOptions = { maxResolution: 1.6, numZoomLevels: 21};
+            var map = new OpenLayers.Map('map', mapOptions);
+            
+            var ol_wms = new OpenLayers.Layer.WMS( "OpenLayers WMS", 
+                "http://labs.metacarta.com/wms/vmap0?", {layers: 'basic'} );
+
+            var ww = new OpenLayers.Layer.WorldWind( "Urban", 
+                "http://worldwind25.arc.nasa.gov/tile/tile.aspx?", .8, 9,
+                {T:"104"}, { tileSize: new OpenLayers.Size(512,512) });
+
+
+            map.addLayers([ol_wms, ww]);
+            map.addControl(new OpenLayers.Control.LayerSwitcher());
+            map.setCenter(new OpenLayers.LonLat(-71.4, 42.3), 6);
+        }
+        // -->
+    </script>
+  </head>
+  <body onload="init()">
+    <h1>OpenLayers Example</h1>
+    <div id="map"></div>
+  </body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/urban.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/ve.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/ve.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/ve.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,65 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <style type="text/css">
+        #map {
+            width: 100%;
+            height: 512px;
+            border: 1px solid black;
+        }
+    </style>
+
+
+    <script src='http://dev.virtualearth.net/mapcontrol/v3/mapcontrol.js'></script>
+
+    <script src="../lib/OpenLayers.js"></script>
+    <script type="text/javascript">
+        <!--
+ 
+        var lon = 5;
+        var lat = 40;
+        var zoom = 15;
+        var map, velayer, layer;
+
+        function init(){
+            map = new OpenLayers.Map( $('map') , 
+            {controls:[new OpenLayers.Control.MouseDefaults()]});
+
+            velayer = new OpenLayers.Layer.VirtualEarth( "VE",
+            { minZoomLevel: 4, maxZoomLevel: 6, 'type': VEMapStyle.Aerial}); 
+            map.addLayer(velayer);
+
+            var twms = new OpenLayers.Layer.WMS( "World Map", 
+                        "http://world.freemap.in/cgi-bin/mapserv?", 
+                        { map: '/www/freemap.in/world/map/factbooktrans.map', 
+                          transparent:'true',
+                          layers: 'factbook', 
+                          'format':'png'} );
+            map.addLayer(twms);
+            
+            markers = new OpenLayers.Layer.Markers("markers");
+            map.addLayer(markers);
+
+            map.setCenter(new OpenLayers.LonLat(lon, lat), zoom);
+            map.addControl( new OpenLayers.Control.LayerSwitcher() );
+            map.addControl( new OpenLayers.Control.PanZoomBar() );
+        }
+        
+        function add() {
+            
+            marker = new OpenLayers.Marker(new OpenLayers.LonLat(2, 41));
+            markers.addMarker(marker);
+        }
+
+        function remove() {
+            markers.removeMarker(marker);
+        }
+  // -->
+    </script>
+  </head>
+  <body onload="init()">
+    <h1>OpenLayers VE Example</h1>
+    <div id="map"></div>
+    <div style="background-color:green" onclick="add()"> click to add the marker to the map</div>
+    <div style="background-color:red" onclick="remove()"> click to remove the marker from the map</div>
+  </body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/ve.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/wfs.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/wfs.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/wfs.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,37 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <style type="text/css">
+        #map {
+            width: 512px;
+            height: 512px;
+            border: 1px solid black;
+        }
+    </style>
+    <script src="../lib/OpenLayers.js"></script>
+    <script type="text/javascript">
+        <!--
+        var map, layer;
+
+        function init(){
+            OpenLayers.ProxyHost="/proxy/?url=";
+            map = new OpenLayers.Map('map');
+            layer = new OpenLayers.Layer.WMS( "OpenLayers WMS", 
+                "http://labs.metacarta.com/wms/vmap0", {layers: 'basic'} );
+            map.addLayer(layer);
+
+            layer = new OpenLayers.Layer.WFS( "Owl Survey",
+                "http://www.bsc-eoc.org/cgi-bin/bsc_ows.asp?",
+                {typename: "OWLS", maxfeatures: 10},
+                { featureClass: OpenLayers.Feature.WFS});
+            map.addLayer(layer);
+            map.addControl(new OpenLayers.Control.LayerSwitcher());
+            map.setCenter(new OpenLayers.LonLat(-100, 60), 3);
+        }
+        // -->
+    </script>
+  </head>
+  <body onload="init()">
+    <h1>OpenLayers Example</h1>
+    <div id="map"></div>
+  </body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/wfs.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/wms.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/wms.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/wms.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,34 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <style type="text/css">
+        #map {
+            width: 800px;
+            height: 475px;
+            border: 1px solid black;
+        }
+    </style>
+    <script src="../lib/OpenLayers.js"></script>
+    <script type="text/javascript">
+        <!--
+        var lon = 5;
+        var lat = 40;
+        var zoom = 5;
+        var map, layer;
+
+        function init(){
+            map = new OpenLayers.Map( $('map') );
+            layer = new OpenLayers.Layer.WMS( "OpenLayers WMS", 
+                    "http://labs.metacarta.com/wms/vmap0", {layers: 'basic'} );
+            map.addLayer(layer);
+
+            map.setCenter(new OpenLayers.LonLat(lon, lat), zoom);
+            map.addControl( new OpenLayers.Control.LayerSwitcher() );
+        }
+        
+        // -->
+    </script>
+  </head>
+  <body onload="init()">
+    <div id="map"></div>
+  </body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/wms.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/wmst.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/wmst.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/wmst.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,42 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <style type="text/css">
+        #map {
+            width: 512px;
+            height: 400px;
+            border: 1px solid black;
+        }
+    </style>
+    <script src="../lib/OpenLayers.js"></script>
+    <script type="text/javascript">
+        <!--
+var map,ia_wms;
+        function init(){
+            map = new OpenLayers.Map('map');
+
+            var ol_wms = new OpenLayers.Layer.WMS( "OpenLayers WMS", 
+                "http://labs.metacarta.com/wms/vmap0?", {layers: 'basic'} );
+
+            var jpl_wms = new OpenLayers.Layer.WMS( "NASA Global Mosaic",
+                "http://wms.jpl.nasa.gov/wms.cgi", 
+                {layers: "modis,global_mosaic"});
+
+	        ia_wms = new OpenLayers.Layer.WMS("Nexrad","http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi?",{layers:"nexrad-n0r-wmst",transparent:true,format:'image/png',time:document.getElementById('time').value});
+
+            jpl_wms.setVisibility(false);
+
+            map.addLayers([ol_wms, jpl_wms, ia_wms]);
+            map.addControl(new OpenLayers.Control.LayerSwitcher());
+            map.zoomToMaxExtent();
+        }
+        // -->
+    </script>
+  </head>
+  <body onload="init()">
+    <h1>OpenLayers Example</h1>
+    <p>WMS-T example: update the times, and the radar image will change. Uses Layer.changeParams. Thanks to David Bitner for the inspiration, the original code, and the kick in the butt!</p>
+    <input type='text' id='time'  value="2005-08-29T13:00:00Z" onChange='ia_wms.mergeNewParams({"time":this.value});' >
+    <div id="map"></div>
+  </body>
+</html>
+


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/wmst.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/worldwind.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/worldwind.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/worldwind.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,42 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <style type="text/css">
+        #map {
+            width: 800px;
+            height: 475px;
+            border: 1px solid black;
+        }
+    </style>
+    <script src="../lib/OpenLayers.js"></script>
+    <script type="text/javascript">
+        <!--
+        var ol_wms, ww, ww2;
+        function init(){
+            var map = new OpenLayers.Map('map', {'maxResolution': .28125, tileSize: new OpenLayers.Size(512, 512)});
+
+            ol_wms = new OpenLayers.Layer.WMS( "OpenLayers WMS", 
+            "http://labs.metacarta.com/wms/vmap0?", {layers: 'basic'} );
+
+            ww = new OpenLayers.Layer.WorldWind( "Bathy", 
+            "http://worldwind25.arc.nasa.gov/tile/tile.aspx?", 36, 4,
+            {T:"bmng.topo.bathy.200406"});
+            ww2 = new OpenLayers.Layer.WorldWind( "LANDSAT", 
+                "http://worldwind25.arc.nasa.gov/tile/tile.aspx", 2.25, 4,
+                {T:"105"});
+
+
+
+            map.addLayers([ol_wms, ww, ww2]);
+            map.addControl(new OpenLayers.Control.LayerSwitcher());
+            map.setCenter(new OpenLayers.LonLat(-71.4, 42.3), 6);
+        }
+        // -->
+    </script>
+  </head>
+  <body onload="init()">
+    <h1>OpenLayers Example</h1>
+    <div id="map"></div>
+    <p>This is a demonstration of using Tiled WorldWind layers. WorldWind requires you to define a "LZTD" -- the 3rd param of the constructor -- and the number of zoom levels it supports. When a worldwind layer is not visible at a given tile level, and empty tile is placed there instead. Note that the maxResolution of the map times 512px, must be a multiple of a power of two different from the LZTD -- in this case, .28125 * 512 is 144, which is 36*4, and 2.25*64.</p>
+    <p>This example has a 'Bathy' layer, visible as you zoom out, and a 'landsat' layer, visible as you zoom in, both visible at zoom level 6.</p>
+  </body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/worldwind.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/xhtml.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/xhtml.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/xhtml.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,23 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+        "http://www.w3.org/TR/2000/REC-xhtml1-20000126/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>XHTML Example</title>
+<script src="../lib/OpenLayers.js" type="text/javascript"></script>
+</head>
+<body style="width:100%">
+  <div style="width:100%; height:500px" id="map"></div>
+  <script defer="defer" type="text/javascript">
+    var map = new OpenLayers.Map('map');
+    var wms = new OpenLayers.Layer.WMS( "OpenLayers WMS", 
+        "http://labs.metacarta.com/wms/vmap0", {layers: 'basic'} );
+    map.addLayer(wms);
+    map.zoomToMaxExtent();
+  </script>
+<p>
+      <a href="http://validator.w3.org/check/referer"><img
+          src="http://www.w3.org/Icons/valid-xhtml10"
+          alt="Valid XHTML 1.0!" height="31" width="88" /></a>
+    </p>
+</body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/xhtml.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/yahoo.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/yahoo.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/yahoo.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,38 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <style type="text/css">
+        #map {
+            width: 512px;
+            height: 512px;
+            border: 1px solid black;
+        }
+    </style>
+    <script src="http://api.maps.yahoo.com/ajaxymap?v=3.0&appid=euzuro-openlayers"></script>
+    <script src="../lib/OpenLayers.js"></script>
+    <script type="text/javascript">
+        <!--
+        var map, layer, velayer;
+
+        function init(){
+            map = new OpenLayers.Map('map');
+
+            layer = new OpenLayers.Layer.WMS( "OpenLayers WMS", 
+                "http://labs.metacarta.com/wms/vmap0", {layers: 'basic'} );
+            map.addLayer(layer);
+
+
+            yahooLayer = new OpenLayers.Layer.Yahoo( "Yahoo"); 
+
+            map.addLayer(yahooLayer);
+
+            map.setCenter(new OpenLayers.LonLat(-5, 40), 4);
+            map.addControl(new OpenLayers.Control.LayerSwitcher());
+        }
+        // -->
+    </script>
+  </head>
+  <body onload="init()">
+    <h1>OpenLayers Example</h1>
+    <div id="map"></div>
+  </body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/yahoo.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/zoomLevels.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/zoomLevels.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/zoomLevels.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,53 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <style type="text/css">
+        #map {
+            width: 800px;
+            height: 475px;
+            border: 1px solid black;
+        }
+    </style>
+    <script src="../lib/OpenLayers.js"></script>
+    <script type="text/javascript">
+        <!--
+        var lon = 50;
+        var lat = 0;
+        var zoom = 0;
+        var map, layer;
+
+        function init(){
+            
+            var options = { 
+//                            resolutions: [1.40625,0.703125,0.3515625,0.17578125,0.087890625,0.0439453125,0.02197265625,0.010986328125,0.0054931640625,0.00274658203125,0.00137329101],
+//                            scales: [50000000, 10000000],
+//                            maxResolution: 0.17578125,
+//                            minResolution: 0.0439453125,
+//                            maxScale: 10000000,
+//                            minScale: 50000000,
+                            minResolution: "auto",
+                            minExtent: new OpenLayers.Bounds(-1, -1, 1, 1), 
+                            maxResolution: "auto",
+                            maxExtent: new OpenLayers.Bounds(-180, -90, 90, 180), 
+//                            numZoomLevels: 5,
+                            controls: [new OpenLayers.Control.MouseDefaults()]
+                          };
+
+            map = new OpenLayers.Map( $('map') , options);
+            
+            map.addControl(new OpenLayers.Control.PanZoomBar());
+
+            layer = new OpenLayers.Layer.WMS( "OpenLayers WMS", 
+                    "http://labs.metacarta.com/wms/vmap0", {layers: 'basic'});
+            map.addLayer(layer);
+
+
+            map.setCenter(new OpenLayers.LonLat(lon, lat), zoom);
+        }
+        
+        // -->
+    </script>
+  </head>
+  <body onload="init()">
+    <div id="map"></div>
+  </body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/examples/zoomLevels.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/blank.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/blank.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/close.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/close.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/drag-rectangle-off.png
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/drag-rectangle-off.png
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/drag-rectangle-on.png
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/drag-rectangle-on.png
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/east-mini.png
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/east-mini.png
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/layer-switcher-maximize.png
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/layer-switcher-maximize.png
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/layer-switcher-minimize.png
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/layer-switcher-minimize.png
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/marker-blue.png
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/marker-blue.png
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/marker-gold.png
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/marker-gold.png
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/marker-green.png
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/marker-green.png
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/marker.png
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/marker.png
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/measuring-stick-off.png
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/measuring-stick-off.png
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/measuring-stick-on.png
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/measuring-stick-on.png
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/north-mini.png
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/north-mini.png
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/panning-hand-off.png
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/panning-hand-off.png
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/panning-hand-on.png
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/panning-hand-on.png
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/slider.png
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/slider.png
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/south-mini.png
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/south-mini.png
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/west-mini.png
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/west-mini.png
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/zoom-minus-mini.png
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/zoom-minus-mini.png
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/zoom-plus-mini.png
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/zoom-plus-mini.png
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/zoom-world-mini.png
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/zoom-world-mini.png
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/zoombar.png
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/img/zoombar.png
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Ajax.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Ajax.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Ajax.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,308 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
+ * for the full text of the license. */
+
+
+OpenLayers.ProxyHost = "";
+//OpenLayers.ProxyHost = "examples/proxy.cgi?url=";
+
+/**
+* Ajax reader for OpenLayers
+*
+*@uri url to do remote XML http get
+*@param 'get' format params (x=y&a=b...)
+*@who object to handle callbacks for this request
+*@complete  the function to be called on success 
+*@failure  the function to be called on failure
+*
+* example usage from a caller:
+*
+*   caps: function(request) {
+*    -blah-  
+*   },
+*
+*   OpenLayers.loadURL(url,params,this,caps);
+*
+* Notice the above example does not provide an error handler; a default empty
+* handler is provided which merely logs the error if a failure handler is not 
+* supplied
+*
+*/
+
+
+/** 
+* @param {} request
+*/
+OpenLayers.nullHandler = function(request) {
+    alert("Unhandled request return " + request.statusText);
+};
+
+/** Background load a document
+*
+* @param {String} uri URI of source doc
+* @param {String} params Params on get (doesnt seem to work)
+* @param {Object} caller object which gets callbacks
+* @param {Function} onComplete callback for success
+* @param {Function} onFailure callback for failure
+*
+* Both callbacks optional (though silly)
+*/
+OpenLayers.loadURL = function(uri, params, caller,
+                                  onComplete, onFailure) {
+
+    if (OpenLayers.ProxyHost && uri.startsWith("http")) {
+        uri = OpenLayers.ProxyHost + escape(uri);
+    }
+
+    var success = (onComplete) ? onComplete.bind(caller)
+                                : OpenLayers.nullHandler;
+
+    var failure = (onFailure) ? onFailure.bind(caller)
+                           : OpenLayers.nullHandler;
+
+    // from prototype.js
+    new OpenLayers.Ajax.Request(uri, 
+                     {   method: 'get', 
+                         parameters: params,
+                         onComplete: success, 
+                         onFailure: failure
+                      }
+                     );
+};
+
+/** Parse XML into a doc structure
+* @param {String} text
+*
+* @returns Parsed Ajax Response ??
+* @type ?
+*/
+OpenLayers.parseXMLString = function(text) {
+
+    //MS sucks, if the server is bad it dies
+    var index = text.indexOf('<');
+    if (index > 0) {
+        text = text.substring(index);
+    }
+
+    var ajaxResponse = OpenLayers.Util.Try(
+        function() {
+            var xmldom = new ActiveXObject('Microsoft.XMLDOM');
+            xmldom.loadXML(text);
+            return xmldom;
+        },
+        function() {
+            return new DOMParser().parseFromString(text, 'text/xml');
+        },
+        function() {
+            var req = new XMLHttpRequest();
+            req.open("GET", "data:" + "text/xml" +
+                     ";charset=utf-8," + encodeURIComponent(text), false);
+            if (req.overrideMimeType) {
+                req.overrideMimeType("text/xml");
+            }
+            req.send(null);
+            return req.responseXML;
+        }
+    );
+
+    return ajaxResponse;
+};
+
+OpenLayers.Ajax = {
+  emptyFunction: function () {},
+
+  getTransport: function() {
+    return OpenLayers.Util.Try(
+      function() {return new ActiveXObject('Msxml2.XMLHTTP')},
+      function() {return new ActiveXObject('Microsoft.XMLHTTP')},
+      function() {return new XMLHttpRequest()}
+    ) || false;
+  },
+
+  activeRequestCount: 0
+};
+
+OpenLayers.Ajax.Responders = {
+  responders: [],
+
+  register: function(responderToAdd) {
+    for (var i = 0; i < this.responders.length; i++)
+        if (responderToAdd == this.responders[i])
+            return;
+    this.responders.push(responderToAdd);
+  },
+
+  dispatch: function(callback, request, transport, json) {
+    for (var i = 0; i < this.responders.length; i++) {
+      responder = this.responders[i];
+      if (responder[callback] && typeof responder[callback] == 'function') {
+        try {
+          responder[callback].apply(responder, [request, transport, json]);
+        } catch (e) {}
+      }
+    }
+  }
+};
+
+OpenLayers.Ajax.Responders.register({
+  onCreate: function() {
+    OpenLayers.Ajax.activeRequestCount++;
+  },
+
+  onComplete: function() {
+    OpenLayers.Ajax.activeRequestCount--;
+  }
+});
+
+OpenLayers.Ajax.Base = function() {};
+OpenLayers.Ajax.Base.prototype = {
+  setOptions: function(options) {
+    this.options = {
+      method:       'post',
+      asynchronous: true,
+      parameters:   ''
+    }
+    OpenLayers.Util.extend(this.options, options || {});
+  },
+
+  responseIsSuccess: function() {
+    return this.transport.status == undefined
+        || this.transport.status == 0
+        || (this.transport.status >= 200 && this.transport.status < 300);
+  },
+
+  responseIsFailure: function() {
+    return !this.responseIsSuccess();
+  }
+}
+
+OpenLayers.Ajax.Request = OpenLayers.Class.create();
+OpenLayers.Ajax.Request.Events =
+  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
+
+OpenLayers.Ajax.Request.prototype = OpenLayers.Class.inherit( OpenLayers.Ajax.Base, {
+  initialize: function(url, options) {
+    this.transport = OpenLayers.Ajax.getTransport();
+    this.setOptions(options);
+    this.request(url);
+  },
+
+  request: function(url) {
+    var parameters = this.options.parameters || '';
+    if (parameters.length > 0) parameters += '&_=';
+
+    try {
+      this.url = url;
+      if (this.options.method == 'get' && parameters.length > 0)
+        this.url += (this.url.match(/\?/) ? '&' : '?') + parameters;
+
+      OpenLayers.Ajax.Responders.dispatch('onCreate', this, this.transport);
+
+      this.transport.open(this.options.method, this.url,
+        this.options.asynchronous);
+
+      if (this.options.asynchronous) {
+        this.transport.onreadystatechange = this.onStateChange.bind(this);
+        setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10);
+      }
+
+      this.setRequestHeaders();
+
+      var body = this.options.postBody ? this.options.postBody : parameters;
+      this.transport.send(this.options.method == 'post' ? body : null);
+
+    } catch (e) {
+      this.dispatchException(e);
+    }
+  },
+
+  setRequestHeaders: function() {
+    var requestHeaders =
+      ['X-Requested-With', 'XMLHttpRequest',
+       'X-Prototype-Version', 'OpenLayers'];
+
+    if (this.options.method == 'post') {
+      requestHeaders.push('Content-type',
+        'application/x-www-form-urlencoded');
+
+      /* Force "Connection: close" for Mozilla browsers to work around
+       * a bug where XMLHttpReqeuest sends an incorrect Content-length
+       * header. See Mozilla Bugzilla #246651.
+       */
+      if (this.transport.overrideMimeType)
+        requestHeaders.push('Connection', 'close');
+    }
+
+    if (this.options.requestHeaders)
+      requestHeaders.push.apply(requestHeaders, this.options.requestHeaders);
+
+    for (var i = 0; i < requestHeaders.length; i += 2)
+      this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]);
+  },
+
+  onStateChange: function() {
+    var readyState = this.transport.readyState;
+    if (readyState != 1)
+      this.respondToReadyState(this.transport.readyState);
+  },
+
+  header: function(name) {
+    try {
+      return this.transport.getResponseHeader(name);
+    } catch (e) {}
+  },
+
+  evalJSON: function() {
+    try {
+      return eval(this.header('X-JSON'));
+    } catch (e) {}
+  },
+
+  evalResponse: function() {
+    try {
+      return eval(this.transport.responseText);
+    } catch (e) {
+      this.dispatchException(e);
+    }
+  },
+
+  respondToReadyState: function(readyState) {
+    var event = OpenLayers.Ajax.Request.Events[readyState];
+    var transport = this.transport, json = this.evalJSON();
+
+    if (event == 'Complete') {
+      try {
+        (this.options['on' + this.transport.status]
+         || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')]
+         || OpenLayers.Ajax.emptyFunction)(transport, json);
+      } catch (e) {
+        this.dispatchException(e);
+      }
+
+      if ((this.header('Content-type') || '').match(/^text\/javascript/i))
+        this.evalResponse();
+    }
+
+    try {
+      (this.options['on' + event] || OpenLayers.Ajax.emptyFunction)(transport, json);
+      OpenLayers.Ajax.Responders.dispatch('on' + event, this, transport, json);
+    } catch (e) {
+      this.dispatchException(e);
+    }
+
+    /* Avoid memory leak in MSIE: clean up the oncomplete event handler */
+    if (event == 'Complete')
+      this.transport.onreadystatechange = OpenLayers.Ajax.emptyFunction;
+  },
+
+  dispatchException: function(exception) {
+    (this.options.onException || OpenLayers.Ajax.emptyFunction)(this, exception);
+    OpenLayers.Ajax.Responders.dispatch('onException', this, exception);
+  }
+});
+
+OpenLayers.Ajax.getElementsByTagNameNS  = function(parentnode, nsuri, nsprefix, tagname) {
+    return parentnode.getElementsByTagNameNS ?
+        parentnode.getElementsByTagNameNS(nsuri, tagname)
+        : parentnode.getElementsByTagName(nsprefix + ':' + tagname);
+}


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Ajax.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/BaseTypes.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/BaseTypes.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/BaseTypes.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,888 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
+ * for the full text of the license. */
+
+
+/* OpenLayers.Class metaclass */
+OpenLayers.Class = {
+    isPrototype: function () {}, // magic anonymous value
+
+    create: function() {
+        return function() {
+            if (arguments && arguments[0] != OpenLayers.Class.isPrototype)
+                this.initialize.apply(this, arguments);
+        }
+    },
+ 
+    inherit: function () {
+        var superClass = arguments[0];
+        var proto = new superClass(OpenLayers.Class.isPrototype);
+        for (var i = 1; i < arguments.length; i++) {
+            if (typeof arguments[i] == "function") {
+                var mixin = arguments[i];
+                arguments[i] = new mixin(OpenLayers.Class.isPrototype);
+            }
+            OpenLayers.Util.extend(proto, arguments[i]);
+        }
+        return proto;
+    }
+};
+
+/*
+    OpenLayers.Class.inherit( OpenLayers.Layer.Grid, OpenLayers.Layer.HTTPRequest, {
+        some stuff
+    });
+*/
+
+/*********************
+ *                   *
+ *      PIXEL        * 
+ *                   * 
+ *********************/
+
+/**
+ * @class 
+ * 
+ * This class represents a screen coordinate, in x and y coordinates
+ */
+OpenLayers.Pixel = OpenLayers.Class.create();
+OpenLayers.Pixel.prototype = {
+    
+    /** @type float */
+    x: 0.0,
+
+    /** @type float */
+    y: 0.0,
+    
+    /** 
+    * @constructor
+    *
+    * @param {float} x
+    * @param {float} y
+    */
+    initialize: function(x, y) {
+        this.x = parseFloat(x);
+        this.y = parseFloat(y);
+    },
+    
+    /**
+    * @return string representation of Pixel. ex: "x=200.4,y=242.2"
+    * @type str
+    */
+    toString:function() {
+        return ("x=" + this.x + ",y=" + this.y);
+    },
+
+    /**
+     * @type OpenLayers.Pixel
+     */
+    clone:function() {
+        return new OpenLayers.Pixel(this.x, this.y); 
+    },
+    
+    /** 
+    * @param {OpenLayers.Pixel} px
+    * 
+    * @return whether or not the point passed in as parameter is equal to this
+    *          note that if px passed in is null, returns false
+    * @type bool
+    */
+    equals:function(px) {
+        var equals = false;
+        if (px != null) {
+            equals = ((this.x == px.x && this.y == px.y) ||
+                      (isNaN(this.x) && isNaN(this.y) && isNaN(px.x) && isNaN(px.y)));
+        }
+        return equals;
+    },
+
+    /**
+    * @param {int} x
+    * @param {int} y
+    * 
+    * @return a new Pixel with this pixel's x&y augmented by the 
+    *         values passed in.
+    * @type OpenLayers.Pixel
+    */
+    add:function(x, y) {
+        return new OpenLayers.Pixel(this.x + x, this.y + y);
+    },
+
+    /**
+    * @param {OpenLayers.Pixel} px
+    * 
+    * @return a new Pixel with this pixel's x&y augmented by the 
+    *         x&y values of the pixel passed in.
+    * @type OpenLayers.Pixel
+    */
+    offset:function(px) {
+        var newPx = this.clone();
+        if (px) {
+            newPx = this.add(px.x, px.y);
+        }
+        return newPx;
+    },
+    
+    /** @final @type str */
+    CLASS_NAME: "OpenLayers.Pixel"
+};
+
+
+/*********************
+ *                   *
+ *      SIZE         * 
+ *                   * 
+ *********************/
+
+
+/**
+* @class 
+* 
+* This class represents a width and height pair
+*/
+OpenLayers.Size = OpenLayers.Class.create();
+OpenLayers.Size.prototype = {
+
+    /** @type float */
+    w: 0.0,
+    
+    /** @type float */
+    h: 0.0,
+
+
+    /** 
+    * @constructor
+    * 
+    * @param {float} w 
+    * @param {float} h 
+    */
+    initialize: function(w, h) {
+        this.w = parseFloat(w);
+        this.h = parseFloat(h);
+    },
+
+    /** 
+    * @return String representation of OpenLayers.Size object. 
+    *         (ex. <i>"w=55,h=66"</i>)
+    * @type String
+    */
+    toString:function() {
+        return ("w=" + this.w + ",h=" + this.h);
+    },
+
+    /** 
+     * @return New OpenLayers.Size object with the same w and h values
+     * @type OpenLayers.Size
+     */
+    clone:function() {
+        return new OpenLayers.Size(this.w, this.h);
+    },
+
+    /** 
+    * @param {OpenLayers.Size} sz
+    * @returns Boolean value indicating whether the passed-in OpenLayers.Size 
+    *          object has the same w and h components as this
+    *          note that if sz passed in is null, returns false
+    *
+    * @type bool
+    */
+    equals:function(sz) {
+        var equals = false;
+        if (sz != null) {
+            equals = ((this.w == sz.w && this.h == sz.h) ||
+                      (isNaN(this.w) && isNaN(this.h) && isNaN(sz.w) && isNaN(sz.h)));
+        }
+        return equals;
+    },
+    
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.Size"
+};
+
+/*********************
+ *                   *
+ *      LONLAT       * 
+ *                   * 
+ *********************/
+
+
+/**
+* @class 
+* 
+* This class represents a longitude and latitude pair
+*/
+OpenLayers.LonLat = OpenLayers.Class.create();
+OpenLayers.LonLat.prototype = {
+
+    /** @type float */
+    lon: 0.0,
+    
+    /** @type float */
+    lat: 0.0,
+
+    /**
+    * @constructor
+    * 
+    * @param {float} lon
+    * @param {float} lat
+    */
+    initialize: function(lon, lat) {
+        this.lon = parseFloat(lon);
+        this.lat = parseFloat(lat);
+    },
+    
+    /** 
+    * @return String representation of OpenLayers.LonLat object. 
+    *         (ex. <i>"lon=5,lat=42"</i>)
+    * @type String
+    */
+    toString:function() {
+        return ("lon=" + this.lon + ",lat=" + this.lat);
+    },
+
+    /** 
+    * @return Shortened String representation of OpenLayers.LonLat object. 
+    *         (ex. <i>"5, 42"</i>)
+    * @type String
+    */
+    toShortString:function() {
+        return (this.lon + ", " + this.lat);
+    },
+
+    /** 
+     * @return New OpenLayers.LonLat object with the same lon and lat values
+     * @type OpenLayers.LonLat
+     */
+    clone:function() {
+        return new OpenLayers.LonLat(this.lon, this.lat);
+    },
+
+    /** 
+    * @param {float} lon
+    * @param {float} lat
+    *
+    * @return A new OpenLayers.LonLat object with the lon and lat passed-in
+    *         added to this's. 
+    * @type OpenLayers.LonLat
+    */
+    add:function(lon, lat) {
+        return new OpenLayers.LonLat(this.lon + lon, this.lat + lat);
+    },
+
+    /** 
+    * @param {OpenLayers.LonLat} ll
+    * @returns Boolean value indicating whether the passed-in OpenLayers.LonLat
+    *          object has the same lon and lat components as this
+    *          note that if ll passed in is null, returns false
+    *
+    * @type bool
+    */
+    equals:function(ll) {
+        var equals = false;
+        if (ll != null) {
+            equals = ((this.lon == ll.lon && this.lat == ll.lat) ||
+                      (isNaN(this.lon) && isNaN(this.lat) && isNaN(ll.lon) && isNaN(ll.lat)));
+        }
+        return equals;
+    },
+    
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.LonLat"
+};
+
+/** Alternative constructor that builds a new OpenLayers.LonLat from a 
+*    parameter string
+* 
+* @constructor
+* 
+* @param {String} str Comma-separated Lon,Lat coordinate string. 
+*                     (ex. <i>"5,40"</i>)
+*
+* @returns New OpenLayers.LonLat object built from the passed-in String.
+* @type OpenLayers.LonLat
+*/
+OpenLayers.LonLat.fromString = function(str) {
+    var pair = str.split(",");
+    return new OpenLayers.LonLat(parseFloat(pair[0]), 
+                                 parseFloat(pair[1]));
+};
+
+
+
+/*********************
+ *                   *
+ *      BOUNDS       * 
+ *                   * 
+ *********************/
+
+
+
+
+/**
+* @class 
+* 
+* This class represents a bounding box. 
+* Data stored as left, bottom, right, top floats
+*/
+OpenLayers.Bounds = OpenLayers.Class.create();
+OpenLayers.Bounds.prototype = {
+
+    /** @type float */
+    left: 0.0,
+
+    /** @type float */
+    bottom: 0.0,
+
+    /** @type float */
+    right: 0.0,
+
+    /** @type float */
+    top: 0.0,    
+
+    /**
+    * @constructor
+    *
+    * @param {float} left
+    * @param {float} bottom
+    * @param {float} right
+    * @param {float} top
+    *
+    */
+    initialize: function(left, bottom, right, top) {
+        this.left = parseFloat(left);
+        this.bottom = parseFloat(bottom);
+        this.right = parseFloat(right);
+        this.top = parseFloat(top);
+    },
+
+    /**
+     * @returns A fresh copy of the bounds
+     * @type OpenLayers.Bounds
+     */
+    clone:function() {
+        return new OpenLayers.Bounds(this.left, this.bottom, 
+                                     this.right, this.top);
+    },
+
+    /** 
+    * @param {OpenLayers.Bounds} bounds
+    * @returns Boolean value indicating whether the passed-in OpenLayers.Bounds
+    *          object has the same left, right, top, bottom components as this
+    *           note that if bounds passed in is null, returns false
+    *
+    * @type bool
+    */
+    equals:function(bounds) {
+        var equals = false;
+        if (bounds != null) {
+            equals = ((this.left == bounds.left) && 
+                      (this.right == bounds.right) &&
+                      (this.top == bounds.top) && 
+                      (this.bottom == bounds.bottom));
+        }
+        return equals;
+    },
+
+    /** 
+    * @return String representation of OpenLayers.Bounds object. 
+    *         (ex.<i>"left-bottom=(5,42) right-top=(10,45)"</i>)
+    * @type String
+    */
+    toString:function(){
+        return ( "left-bottom=(" + this.left + "," + this.bottom + ")"
+                 + " right-top=(" + this.right + "," + this.top + ")" );
+    },
+
+    /** 
+     * @param {int} decimal How many significant digits in the bbox coords?
+     *                      Default is 6
+     * 
+     * @returns Simple String representation of OpenLayers.Bounds object.
+     *         (ex. <i>"5,42,10,45"</i>)
+     * @type String
+     */
+    toBBOX:function(decimal) {
+        if (decimal== null) {
+            decimal = 6; 
+        }
+        var mult = Math.pow(10, decimal);
+        var bbox = Math.round(this.left * mult) / mult + "," + 
+                   Math.round(this.bottom * mult) / mult + "," + 
+                   Math.round(this.right * mult) / mult + "," + 
+                   Math.round(this.top * mult) / mult;
+
+        return bbox;
+    },
+    
+    /**
+    * @returns The width of the bounds
+    * @type float
+    */
+    getWidth:function() {
+        return (this.right - this.left);
+    },
+
+    /**
+    * @returns The height of the bounds
+    * @type float
+    */
+    getHeight:function() {
+        return (this.top - this.bottom);
+    },
+
+    /**
+    * @returns An OpenLayers.Size which represents the size of the box
+    * @type OpenLayers.Size
+    */
+    getSize:function() {
+        return new OpenLayers.Size(this.getWidth(), this.getHeight());
+    },
+
+    /**
+    * @returns An OpenLayers.Pixel which represents the center of the bounds
+    * @type OpenLayers.Pixel
+    */
+    getCenterPixel:function() {
+        return new OpenLayers.Pixel( (this.left + this.right) / 2,
+                                     (this.bottom + this.top) / 2);
+    },
+
+    /**
+    * @returns An OpenLayers.LonLat which represents the center of the bounds
+    * @type OpenLayers.LonLat
+    */
+    getCenterLonLat:function() {
+        return new OpenLayers.LonLat( (this.left + this.right) / 2,
+                                      (this.bottom + this.top) / 2);
+    },
+
+    /**
+    * @param {float} x
+    * @param {float} y
+    *
+    * @returns A new OpenLayers.Bounds whose coordinates are the same as this, 
+    *          but shifted by the passed-in x and y values
+    * @type OpenLayers.Bounds
+    */
+    add:function(x, y){
+        return new OpenLayers.Bounds(this.left + x, this.bottom + y,
+                                     this.right + x, this.top + y);
+    },
+
+    /**
+     * @param {OpenLayers.LonLat} ll
+     * @param {Boolean} inclusive Whether or not to include the border. 
+     *                            Default is true
+     *
+     * @return Whether or not the passed-in lonlat is within this bounds
+     * @type Boolean
+     */
+    containsLonLat:function(ll, inclusive) {
+        return this.contains(ll.lon, ll.lat, inclusive);
+    },
+
+    /**
+     * @param {OpenLayers.Pixel} px
+     * @param {Boolean} inclusive Whether or not to include the border. 
+     *                            Default is true
+     *
+     * @return Whether or not the passed-in pixel is within this bounds
+     * @type Boolean
+     */
+    containsPixel:function(px, inclusive) {
+        return this.contains(px.x, px.y, inclusive);
+    },
+    
+    /**
+    * @param {float} x
+    * @param {float} y
+    * @param {Boolean} inclusive Whether or not to include the border. 
+    *                            Default is true
+    *
+    * @return Whether or not the passed-in coordinates are within this bounds
+    * @type Boolean
+    */
+    contains:function(x, y, inclusive) {
+    
+        //set default
+        if (inclusive == null) {
+            inclusive = true;
+        }
+        
+        var contains = false;
+        if (inclusive) {
+            contains = ((x >= this.left) && (x <= this.right) && 
+                        (y >= this.bottom) && (y <= this.top));
+        } else {
+            contains = ((x > this.left) && (x < this.right) && 
+                        (y > this.bottom) && (y < this.top));
+        }              
+        return contains;
+    },
+
+    /**
+     * @param {OpenLayers.Bounds} bounds
+     * @param {Boolean} inclusive Whether or not to include the border. 
+     *                            Default is true
+     *
+     * @return Whether or not the passed-in OpenLayers.Bounds object intersects
+     *         this bounds. Simple math just check if either contains the other, 
+     *         allowing for partial.
+     * @type Boolean
+     */
+    intersectsBounds:function(bounds, inclusive) {
+
+        if (inclusive == null) {
+            inclusive = true;
+        }
+        var inBottom = (bounds.bottom == this.bottom && bounds.top == this.top) ?
+                    true : (((bounds.bottom > this.bottom) && (bounds.bottom < this.top)) || 
+                           ((this.bottom > bounds.bottom) && (this.bottom < bounds.top))); 
+        var inTop = (bounds.bottom == this.bottom && bounds.top == this.top) ?
+                    true : (((bounds.top > this.bottom) && (bounds.top < this.top)) ||
+                           ((this.top > bounds.bottom) && (this.top < bounds.top))); 
+        var inRight = (bounds.right == this.right && bounds.left == this.left) ?
+                    true : (((bounds.right > this.left) && (bounds.right < this.right)) ||
+                           ((this.right > bounds.left) && (this.right < bounds.right))); 
+        var inLeft = (bounds.right == this.right && bounds.left == this.left) ?
+                    true : (((bounds.left > this.left) && (bounds.left < this.right)) || 
+                           ((this.left > bounds.left) && (this.left < bounds.right))); 
+
+        return (this.containsBounds(bounds, true, inclusive) ||
+                bounds.containsBounds(this, true, inclusive) ||
+                ((inTop || inBottom ) && (inLeft || inRight )));
+    },
+    
+    /**
+    * @param {OpenLayers.Bounds} bounds
+    * @param {Boolean} partial If true, only part of passed-in 
+    *                          OpenLayers.Bounds needs be within this bounds. 
+    *                          If false, the entire passed-in bounds must be
+    *                          within. Default is false
+    * @param {Boolean} inclusive Whether or not to include the border. 
+    *                            Default is true
+    *
+    * @return Whether or not the passed-in OpenLayers.Bounds object is 
+    *         contained within this bounds. 
+    * @type Boolean
+    */
+    containsBounds:function(bounds, partial, inclusive) {
+
+        //set defaults
+        if (partial == null) {
+            partial = false;
+        }
+        if (inclusive == null) {
+            inclusive = true;
+        }
+
+        var inLeft;
+        var inTop;
+        var inRight;
+        var inBottom;
+        
+        if (inclusive) {
+            inLeft = (bounds.left >= this.left) && (bounds.left <= this.right);
+            inTop = (bounds.top >= this.bottom) && (bounds.top <= this.top);
+            inRight= (bounds.right >= this.left) && (bounds.right <= this.right);
+            inBottom = (bounds.bottom >= this.bottom) && (bounds.bottom <= this.top);
+        } else {
+            inLeft = (bounds.left > this.left) && (bounds.left < this.right);
+            inTop = (bounds.top > this.bottom) && (bounds.top < this.top);
+            inRight= (bounds.right > this.left) && (bounds.right < this.right);
+            inBottom = (bounds.bottom > this.bottom) && (bounds.bottom < this.top);
+        }
+        
+        return (partial) ? (inTop || inBottom ) && (inLeft || inRight ) 
+                         : (inTop && inLeft && inBottom && inRight);
+    },
+
+    /** 
+     * @param {OpenLayers.LonLat} lonlat
+     *
+     * @returns The quadrant ("br" "tr" "tl" "bl") of the bounds in which 
+     *           the coordinate lies.
+     * @type String
+     */
+    determineQuadrant: function(lonlat) {
+    
+        var quadrant = "";
+        var center = this.getCenterLonLat();
+        
+        quadrant += (lonlat.lat < center.lat) ? "b" : "t";
+        quadrant += (lonlat.lon < center.lon) ? "l" : "r";
+    
+        return quadrant; 
+    },
+
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.Bounds"
+};
+
+/** Alternative constructor that builds a new OpenLayers.Bounds from a 
+*    parameter string
+* 
+* @constructor
+* 
+* @param {String} str Comma-separated bounds string. (ex. <i>"5,42,10,45"</i>)
+*
+* @returns New OpenLayers.Bounds object built from the passed-in String.
+* @type OpenLayers.Bounds
+*/
+OpenLayers.Bounds.fromString = function(str) {
+    var bounds = str.split(",");
+    return OpenLayers.Bounds.fromArray(bounds);
+};
+
+/** Alternative constructor that builds a new OpenLayers.Bounds
+*    from an array
+* 
+* @constructor
+* 
+* @param {Array} bbox Array of bounds values (ex. <i>[5,42,10,45]</i>)
+*
+* @returns New OpenLayers.Bounds object built from the passed-in Array.
+* @type OpenLayers.Bounds
+*/
+OpenLayers.Bounds.fromArray = function(bbox) {
+    return new OpenLayers.Bounds(parseFloat(bbox[0]),
+                                 parseFloat(bbox[1]),
+                                 parseFloat(bbox[2]),
+                                 parseFloat(bbox[3]));
+};
+
+/** Alternative constructor that builds a new OpenLayers.Bounds
+*    from an OpenLayers.Size
+* 
+* @constructor
+* 
+* @param {OpenLayers.Size} size
+*            
+* @returns New OpenLayers.Bounds object built with top and left set to 0 and
+*           bottom right taken from the passed-in OpenLayers.Size.
+* @type OpenLayers.Bounds
+*/
+OpenLayers.Bounds.fromSize = function(size) {
+    return new OpenLayers.Bounds(0,
+                                 size.h,
+                                 size.w,
+                                 0);
+};
+/**
+ * @param {String} quadrant 
+ * 
+ * @returns The opposing quadrant ("br" "tr" "tl" "bl"). For Example, if 
+ *           you pass in "bl" it returns "tr", if you pass in "br" it 
+ *           returns "tl", etc.
+ * @type String
+ */
+OpenLayers.Bounds.oppositeQuadrant = function(quadrant) {
+    var opp = "";
+    
+    opp += (quadrant.charAt(0) == 't') ? 'b' : 't';
+    opp += (quadrant.charAt(1) == 'l') ? 'r' : 'l';
+    
+    return opp;
+};
+
+
+/*********************
+ *                   *
+ *      ELEMENT      * 
+ *                   * 
+ *********************/
+
+OpenLayers.Element = {
+  visible: function(element) {
+    return $(element).style.display != 'none';
+  },
+
+  toggle: function() {
+    for (var i = 0; i < arguments.length; i++) {
+      var element = $(arguments[i]);
+      OpenLayers.Element[OpenLayers.Element.visible(element) ? 'hide' : 'show'](element);
+    }
+  },
+
+  hide: function() {
+    for (var i = 0; i < arguments.length; i++) {
+      var element = $(arguments[i]);
+      element.style.display = 'none';
+    }
+  },
+
+  show: function() {
+    for (var i = 0; i < arguments.length; i++) {
+      var element = $(arguments[i]);
+      element.style.display = '';
+    }
+  },
+
+  remove: function(element) {
+    element = $(element);
+    element.parentNode.removeChild(element);
+  },
+
+  getHeight: function(element) {
+    element = $(element);
+    return element.offsetHeight;
+  },
+
+  getDimensions: function(element) {
+    element = $(element);
+    if (OpenLayers.Element.getStyle(element, 'display') != 'none')
+      return {width: element.offsetWidth, height: element.offsetHeight};
+
+    // All *Width and *Height properties give 0 on elements with display none,
+    // so enable the element temporarily
+    var els = element.style;
+    var originalVisibility = els.visibility;
+    var originalPosition = els.position;
+    els.visibility = 'hidden';
+    els.position = 'absolute';
+    els.display = '';
+    var originalWidth = element.clientWidth;
+    var originalHeight = element.clientHeight;
+    els.display = 'none';
+    els.position = originalPosition;
+    els.visibility = originalVisibility;
+    return {width: originalWidth, height: originalHeight};
+  },
+
+  getStyle: function(element, style) {
+    element = $(element);
+    var value = element.style[style.camelize()];
+    if (!value) {
+      if (document.defaultView && document.defaultView.getComputedStyle) {
+        var css = document.defaultView.getComputedStyle(element, null);
+        value = css ? css.getPropertyValue(style) : null;
+      } else if (element.currentStyle) {
+        value = element.currentStyle[style.camelize()];
+      }
+    }
+
+    if (window.opera && OpenLayers.Util.indexOf(['left', 'top', 'right', 'bottom'],style) != -1)
+      if (OpenLayers.Element.getStyle(element, 'position') == 'static') value = 'auto';
+
+    return value == 'auto' ? null : value;
+  }
+
+};
+
+/*********************
+ *                   *
+ *      STRING       * 
+ *                   * 
+ *********************/
+
+/**
+* @param {String} sStart
+* 
+* @returns Whether or not this string starts with the string passed in.
+* @type Boolean
+*/
+String.prototype.startsWith = function(sStart){
+    return (this.substr(0,sStart.length) == sStart);
+};
+
+/**
+* @param {String} str
+* 
+* @returns Whether or not this string contains with the string passed in.
+* @type Boolean
+*/
+String.prototype.contains = function(str){
+    return (this.indexOf(str) != -1);
+};
+
+/**
+* @returns A trimmed version of the string - all leading and 
+*          trailing spaces removed
+* @type String
+*/
+String.prototype.trim = function() {
+    
+    var b = 0;
+    while(this.substr(b,1) == " ") {
+        b++;
+    }
+    
+    var e = this.length - 1;
+    while(this.substr(e,1) == " ") {
+        e--;
+    }
+    
+    return this.substring(b, e+1);
+};
+
+
+String.indexOf = function(object) {
+ for (var i = 0; i < this.length; i++)
+     if (this[i] == object) return i;
+ return -1;
+};
+
+String.prototype.camelize = function() {
+    var oStringList = this.split('-');
+    if (oStringList.length == 1) return oStringList[0];
+
+    var camelizedString = this.indexOf('-') == 0
+      ? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1)
+      : oStringList[0];
+
+    for (var i = 1, len = oStringList.length; i < len; i++) {
+      var s = oStringList[i];
+      camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
+    }
+
+    return camelizedString;
+};
+
+
+/*********************
+ *                   *
+ *      NUMBER       * 
+ *                   * 
+ *********************/
+
+/** NOTE: Works only with integer values does *not* work with floats!
+ * 
+ * @param {int} sig
+ * 
+ * @returns The number, rounded to the specified number of significant digits.
+ *          If null, 0, or negaive value passed in, returns 0
+ * @type int
+ */
+Number.prototype.limitSigDigs = function(sig) {
+    var number = (sig > 0) ? this.toString() : 0;
+    if (sig < number.length) {
+        var exp = number.length - sig;
+        number = Math.round( this / Math.pow(10, exp)) * Math.pow(10, exp);
+    }
+    return parseInt(number);
+}
+
+
+/*********************
+ *                   *
+ *      FUNCTION     * 
+ *                   * 
+ *********************/
+
+Function.prototype.bind = function() {
+  var __method = this, args = [], object = arguments[0];
+  for (var i = 1; i < arguments.length; i++)
+    args.push(arguments[i]);
+  return function(moreargs) {
+    for (var i = 0; i < arguments.length; i++)
+      args.push(arguments[i]);
+    return __method.apply(object, args);
+  }
+};
+
+Function.prototype.bindAsEventListener = function(object) {
+  var __method = this;
+  return function(event) {
+    return __method.call(object, event || window.event);
+  }
+};


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/BaseTypes.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/ArgParser.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/ArgParser.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/ArgParser.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,114 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
+ * for the full text of the license. */
+
+
+/**
+ * @class
+ * 
+ * @requires OpenLayers/Control.js
+ */
+OpenLayers.Control.ArgParser = OpenLayers.Class.create();
+OpenLayers.Control.ArgParser.prototype = 
+  OpenLayers.Class.inherit( OpenLayers.Control, {
+
+    /** @type OpenLayers.LonLat */
+    center: null,
+    
+    /** @type int */
+    zoom: null,
+
+    /** @type Array */
+    layers: null,
+
+    /**
+     * @constructor
+     * 
+     * @param {DOMElement} element
+     * @param {String} base
+     */
+    initialize: function(element, base) {
+        OpenLayers.Control.prototype.initialize.apply(this, arguments);
+    },
+
+    /** Set the map property for the control. 
+     * 
+     * @param {OpenLayers.Map} map
+     */
+    setMap: function(map) {
+        OpenLayers.Control.prototype.setMap.apply(this, arguments);
+
+        //make sure we dont already have an arg parser attached
+        for(var i=0; i< this.map.controls.length; i++) {
+            var control = this.map.controls[i];
+            if ( (control != this) &&
+                 (control.CLASS_NAME == "OpenLayers.Control.ArgParser") ) {
+                break;
+            }
+        }
+        if (i == this.map.controls.length) {
+
+            var args = OpenLayers.Util.getArgs();
+            if (args.lat && args.lon) {
+                this.center = new OpenLayers.LonLat(parseFloat(args.lon),
+                                                    parseFloat(args.lat));
+                if (args.zoom) {
+                    this.zoom = parseInt(args.zoom);
+                }
+    
+                // when we add a new baselayer to see when we can set the center
+                this.map.events.register('changebaselayer', this, 
+                                         this.setCenter);
+                this.setCenter();
+            }
+    
+            if (args.layers) {
+                this.layers = args.layers;
+    
+                // when we add a new layer, set its visibility 
+                this.map.events.register('addlayer', this, 
+                                         this.configureLayers);
+                this.configureLayers();
+            }
+        }
+    },
+   
+    /** As soon as a baseLayer has been loaded, we center and zoom
+     *   ...and remove the handler.
+     */
+    setCenter: function() {
+        
+        if (this.map.baseLayer) {
+            //dont need to listen for this one anymore
+            this.map.events.unregister('changebaselayer', this, 
+                                       this.setCenter);
+                                       
+            this.map.setCenter(this.center, this.zoom);
+        }
+    },
+
+    /** As soon as all the layers are loaded, cycle through them and 
+     *   hide or show them. 
+     */
+    configureLayers: function() {
+
+        if (this.layers.length == this.map.layers.length) { 
+            this.map.events.unregister('addlayer', this, this.configureLayers);
+
+            for(var i=0; i < this.layers.length; i++) {
+                
+                var layer = this.map.layers[i];
+                var c = this.layers.charAt(i);
+                
+                if (c == "B") {
+                    this.map.setBaseLayer(layer);
+                } else if ( (c == "T") || (c == "F") ) {
+                    layer.setVisibility(c == "T");
+                }
+            }
+        }
+    },     
+    
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.Control.ArgParser"
+});


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/ArgParser.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/KeyboardDefaults.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/KeyboardDefaults.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/KeyboardDefaults.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,56 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
+ * for the full text of the license. */
+
+
+/**
+ * @class
+ * 
+ * @requires OpenLayers/Control.js
+ */
+OpenLayers.Control.KeyboardDefaults = OpenLayers.Class.create();
+OpenLayers.Control.KeyboardDefaults.prototype = 
+  OpenLayers.Class.inherit( OpenLayers.Control, {
+
+    /** @type int */
+    slideFactor: 50,
+
+    /**
+     * @constructor
+     */
+    initialize: function() {
+        OpenLayers.Control.prototype.initialize.apply(this, arguments);
+    },
+    
+    /**
+     * 
+     */
+    draw: function() {
+        OpenLayers.Event.observe(document, 
+                      'keypress', 
+                      this.defaultKeyDown.bind(this));
+    },
+    
+    /**
+    * @param {Event} evt
+    */
+    defaultKeyDown: function (evt) {
+        switch(evt.keyCode) {
+            case OpenLayers.Event.KEY_LEFT:
+                this.map.pan(-50, 0);
+                break;
+            case OpenLayers.Event.KEY_RIGHT: 
+                this.map.pan(50, 0);
+                break;
+            case OpenLayers.Event.KEY_UP:
+                this.map.pan(0, -50);
+                break;
+            case OpenLayers.Event.KEY_DOWN:
+                this.map.pan(0, 50);
+                break;
+        }
+    },
+    
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.Control.KeyboardDefaults"
+});


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/KeyboardDefaults.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/LayerSwitcher.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/LayerSwitcher.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/LayerSwitcher.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,443 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
+ * for the full text of the license. */
+
+/** 
+ * @class
+ * 
+ * @requires OpenLayers/Control.js
+ */
+OpenLayers.Control.LayerSwitcher = OpenLayers.Class.create();
+OpenLayers.Control.LayerSwitcher.prototype = 
+  OpenLayers.Class.inherit( OpenLayers.Control, {
+
+    /** @type String */
+    activeColor: "darkblue",
+    
+
+  // DOM Elements
+  
+    /** @type DOMElement */
+    layersDiv: null,
+    
+    /** @type DOMElement */
+    baseLayersDiv: null,
+
+    /** @type Array */
+    baseLayerInputs: null,
+    
+    
+    /** @type DOMElement */
+    dataLbl: null,
+    
+    /** @type DOMElement */
+    dataLayersDiv: null,
+
+    /** @type Array */
+    dataLayerInputs: null,
+
+
+    /** @type DOMElement */
+    minimizeDiv: null,
+
+    /** @type DOMElement */
+    maximizeDiv: null,
+    
+    /** @type Boolean */
+    ascending: true,
+ 
+    /**
+    * @constructor
+    */
+    initialize: function(options) {
+        OpenLayers.Control.prototype.initialize.apply(this, arguments);
+    },
+
+    /** 
+     * @param {OpenLayers.Map} map
+     */
+    setMap: function(map) {
+        OpenLayers.Control.prototype.setMap.apply(this, arguments);
+
+        this.map.events.register("addlayer", this, this.redraw);
+        this.map.events.register("changelayer", this, this.redraw);
+        this.map.events.register("removelayer", this, this.redraw);
+        this.map.events.register("changebaselayer", this, this.redraw);
+    },
+
+    /**
+    * @returns A reference to the DIV DOMElement containing the switcher tabs
+    * @type DOMElement
+    */  
+    draw: function() {
+        OpenLayers.Control.prototype.draw.apply(this);
+
+        // create layout divs
+        this.loadContents();
+
+        // set mode to minimize
+        this.minimizeControl();
+        
+        // populate div with current info
+        this.redraw();    
+
+        return this.div;
+    },
+
+    /** Goes through and takes the current state of the Map and rebuilds the
+     *   control to display that state. Groups base layers into a radio-button
+     *   group and lists each data layer with a checkbox.
+     * 
+     * @returns A reference to the DIV DOMElement containing the control
+     * @type DOMElement
+     */  
+    redraw: function() {
+
+        //clear out previous layers 
+        this.baseLayersDiv.innerHTML = "";
+        this.baseLayerInputs = new Array();
+        
+        this.dataLayersDiv.innerHTML = "";
+        this.dataLayerInputs = new Array();
+        
+        var containsOverlays = false;
+        
+        var layers = this.map.layers.slice();
+        if (!this.ascending) { layers.reverse(); }
+        for( var i = 0; i < layers.length; i++) {
+            var layer = layers[i];
+            var baseLayer = layer.isBaseLayer;
+
+            if (baseLayer || layer.displayInLayerSwitcher) {
+
+                if (!baseLayer) {
+                    containsOverlays = true;
+                }
+
+                // only check a baselayer if it is *the* baselayer, check data
+                //  layers if they are visible
+                var checked = (baseLayer) ? (layer == this.map.baseLayer)
+                                          : layer.getVisibility();
+    
+                // create input element
+                var inputElem = document.createElement("input");
+                inputElem.id = "input_" + layer.name;
+                inputElem.name = (baseLayer) ? "baseLayers" : layer.name;
+                inputElem.type = (baseLayer) ? "radio" : "checkbox";
+                inputElem.value = layer.name;
+                inputElem.checked = checked;
+                inputElem.defaultChecked = checked;
+                inputElem.layer = layer;
+                inputElem.control = this;
+
+                if (!baseLayer && !layer.inRange) {
+                    inputElem.disabled = true;
+                }
+                OpenLayers.Event.observe(inputElem, "mouseup", 
+                              this.onInputClick.bindAsEventListener(inputElem));
+                
+                // create span
+                var labelSpan = document.createElement("span");
+                if (!baseLayer && !layer.inRange) {
+                    labelSpan.style.color = "gray";
+                }
+                labelSpan.innerHTML = layer.name;
+                labelSpan.style.verticalAlign = (baseLayer) ? "bottom" : "baseline";
+                OpenLayers.Event.observe(labelSpan, "click", 
+                              this.onInputClick.bindAsEventListener(inputElem));
+                // create line break
+                var br = document.createElement("br");
+    
+                
+                var groupArray = (baseLayer) ? this.baseLayerInputs
+                                             : this.dataLayerInputs;
+                groupArray.push(inputElem);
+                                                     
+    
+                var groupDiv = (baseLayer) ? this.baseLayersDiv
+                                           : this.dataLayersDiv;
+                groupDiv.appendChild(inputElem);
+                groupDiv.appendChild(labelSpan);
+                groupDiv.appendChild(br);
+            }
+        }
+
+        // if no overlays, dont display the overlay label
+        this.dataLbl.style.display = (containsOverlays) ? "" : "none";        
+
+        return this.div;
+    },
+
+    /** A label has been clicked, check or uncheck its corresponding input
+     * 
+     * @private
+     * 
+     * @param {Event} e
+     */
+
+    onInputClick: function(e) {
+        if (!this.disabled) {
+            if (this.type == "radio") {
+                this.checked = true;
+                this.layer.map.setBaseLayer(this.layer, true);
+                this.layer.map.events.triggerEvent("changebaselayer");
+            } else {
+                this.checked = !this.checked;
+                this.control.updateMap();
+            }
+        }
+        OpenLayers.Event.stop(e);
+    },
+    
+    /** Need to update the map accordingly whenever user clicks in either of
+     *   the layers.
+     * 
+     * @private
+     * 
+     * @param {Event} e
+     */
+    onLayerClick: function(e) {
+        this.updateMap();
+    },
+
+
+    /** Cycles through the loaded data and base layer input arrays and makes
+     *   the necessary calls to the Map object such that that the map's 
+     *   visual state corresponds to what the user has selected in the control
+     * 
+     * @private
+     */
+    updateMap: function() {
+
+        // set the newly selected base layer        
+        for(var i=0; i < this.baseLayerInputs.length; i++) {
+            var input = this.baseLayerInputs[i];   
+            if (input.checked) {
+                this.map.setBaseLayer(input.layer, false);
+            }
+        }
+
+        // set the correct visibilities for the overlays
+        for(var i=0; i < this.dataLayerInputs.length; i++) {
+            var input = this.dataLayerInputs[i];   
+            input.layer.setVisibility(input.checked, true);
+        }
+
+    },
+
+    /** Set up the labels and divs for the control
+     * 
+     * @param {Event} e
+     */
+    maximizeControl: function(e) {
+
+        //HACK HACK HACK - find a way to auto-size this layerswitcher
+        this.div.style.width = "20em";
+        this.div.style.height = "";
+
+        this.showControls(false);
+
+        if (e != null) {
+            OpenLayers.Event.stop(e);                                            
+        }
+    },
+    
+    /** Hide all the contents of the control, shrink the size, 
+     *   add the maximize icon
+     * 
+     * @param {Event} e
+     */
+    minimizeControl: function(e) {
+
+        this.div.style.width = "0px";
+        this.div.style.height = "0px";
+
+        this.showControls(true);
+
+        if (e != null) {
+            OpenLayers.Event.stop(e);                                            
+        }
+    },
+
+    /** Hide/Show all LayerSwitcher controls depending on whether we are
+     *   minimized or not
+     * 
+     * @private
+     * 
+     * @param {Boolean} minimize
+     */
+    showControls: function(minimize) {
+
+        this.maximizeDiv.style.display = minimize ? "" : "none";
+        this.minimizeDiv.style.display = minimize ? "none" : "";
+
+        this.layersDiv.style.display = minimize ? "none" : "";
+    },
+    
+    /** Set up the labels and divs for the control
+     * 
+     */
+    loadContents: function() {
+
+        //configure main div
+        this.div.style.position = "absolute";
+        this.div.style.top = "10px";
+        this.div.style.right = "0px";
+        this.div.style.left = "";
+        this.div.style.fontFamily = "sans-serif";
+        this.div.style.fontWeight = "bold";
+        this.div.style.marginTop = "3px";
+        this.div.style.marginLeft = "3px";
+        this.div.style.marginBottom = "3px";
+        this.div.style.fontSize = "smaller";   
+        this.div.style.color = "white";   
+        this.div.style.backgroundColor = "transparent";
+    
+        OpenLayers.Event.observe(this.div, "mouseup", 
+                      this.mouseUp.bindAsEventListener(this));
+        OpenLayers.Event.observe(this.div, "click",
+                      this.ignoreEvent);
+        OpenLayers.Event.observe(this.div, "mousedown",
+                      this.mouseDown.bindAsEventListener(this));
+        OpenLayers.Event.observe(this.div, "dblclick", this.ignoreEvent);
+
+
+        // layers list div        
+        this.layersDiv = document.createElement("div");
+        this.layersDiv.id = "layersDiv";
+        this.layersDiv.style.paddingTop = "5px";
+        this.layersDiv.style.paddingLeft = "10px";
+        this.layersDiv.style.paddingBottom = "5px";
+        this.layersDiv.style.paddingRight = "75px";
+        this.layersDiv.style.backgroundColor = this.activeColor;        
+
+        // had to set width/height to get transparency in IE to work.
+        // thanks -- http://jszen.blogspot.com/2005/04/ie6-opacity-filter-caveat.html
+        //
+        this.layersDiv.style.width = "100%";
+        this.layersDiv.style.height = "100%";
+
+
+        var baseLbl = document.createElement("div");
+        baseLbl.innerHTML = "<u>Base Layer</u>";
+        baseLbl.style.marginTop = "3px";
+        baseLbl.style.marginLeft = "3px";
+        baseLbl.style.marginBottom = "3px";
+        
+        this.baseLayersDiv = document.createElement("div");
+        this.baseLayersDiv.style.paddingLeft = "10px";
+        /*OpenLayers.Event.observe(this.baseLayersDiv, "click", 
+                      this.onLayerClick.bindAsEventListener(this));
+        */
+                     
+
+        this.dataLbl = document.createElement("div");
+        this.dataLbl.innerHTML = "<u>Overlays</u>";
+        this.dataLbl.style.marginTop = "3px";
+        this.dataLbl.style.marginLeft = "3px";
+        this.dataLbl.style.marginBottom = "3px";
+        
+        this.dataLayersDiv = document.createElement("div");
+        this.dataLayersDiv.style.paddingLeft = "10px";
+
+        if (this.ascending) {
+            this.layersDiv.appendChild(baseLbl);
+            this.layersDiv.appendChild(this.baseLayersDiv);
+            this.layersDiv.appendChild(this.dataLbl);
+            this.layersDiv.appendChild(this.dataLayersDiv);
+        } else {
+            this.layersDiv.appendChild(this.dataLbl);
+            this.layersDiv.appendChild(this.dataLayersDiv);
+            this.layersDiv.appendChild(baseLbl);
+            this.layersDiv.appendChild(this.baseLayersDiv);
+        }    
+ 
+        this.div.appendChild(this.layersDiv);
+
+        Rico.Corner.round(this.div, {corners: "tl bl",
+                                      bgColor: "transparent",
+                                      color: this.activeColor,
+                                      blend: false});
+
+        Rico.Corner.changeOpacity(this.layersDiv, 0.75);
+
+        var imgLocation = OpenLayers.Util.getImagesLocation();
+        var sz = new OpenLayers.Size(18,18);        
+
+        // maximize button div
+        var img = imgLocation + 'layer-switcher-maximize.png';
+        this.maximizeDiv = OpenLayers.Util.createAlphaImageDiv(
+                                    "OpenLayers_Control_MaximizeDiv", 
+                                    null, 
+                                    sz, 
+                                    img, 
+                                    "absolute");
+        this.maximizeDiv.style.top = "5px";
+        this.maximizeDiv.style.right = "0px";
+        this.maximizeDiv.style.left = "";
+        this.maximizeDiv.style.display = "none";
+        OpenLayers.Event.observe(this.maximizeDiv, 
+                      "click", 
+                      this.maximizeControl.bindAsEventListener(this));
+        
+        this.div.appendChild(this.maximizeDiv);
+
+        // minimize button div
+        var img = imgLocation + 'layer-switcher-minimize.png';
+        var sz = new OpenLayers.Size(18,18);        
+        this.minimizeDiv = OpenLayers.Util.createAlphaImageDiv(
+                                    "OpenLayers_Control_MinimizeDiv", 
+                                    null, 
+                                    sz, 
+                                    img, 
+                                    "absolute");
+        this.minimizeDiv.style.top = "5px";
+        this.minimizeDiv.style.right = "0px";
+        this.minimizeDiv.style.left = "";
+        this.minimizeDiv.style.display = "none";
+        OpenLayers.Event.observe(this.minimizeDiv, 
+                      "click", 
+                      this.minimizeControl.bindAsEventListener(this));
+
+        this.div.appendChild(this.minimizeDiv);
+    },
+    
+    /** 
+     * @private
+     *
+     * @param {Event} evt
+     */
+    ignoreEvent: function(evt) {
+        OpenLayers.Event.stop(evt);
+    },
+
+    /** Register a local 'mouseDown' flag so that we'll know whether or not
+     *   to ignore a mouseUp event
+     * 
+     * @private
+     *
+     * @param {Event} evt
+     */
+    mouseDown: function(evt) {
+        this.mouseDown = true;
+        this.ignoreEvent(evt);
+    },
+
+    /** If the 'mouseDown' flag has been set, that means that the drag was 
+     *   started from within the LayerSwitcher control, and thus we can 
+     *   ignore the mouseup. Otherwise, let the Event continue.
+     *  
+     * @private
+     *
+     * @param {Event} evt
+     */
+    mouseUp: function(evt) {
+        if (this.mouseDown) {
+            this.mouseDown = false;
+            this.ignoreEvent(evt);
+        }
+    },
+
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.Control.LayerSwitcher"
+});


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/LayerSwitcher.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/MouseDefaults.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/MouseDefaults.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/MouseDefaults.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,257 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
+ * for the full text of the license. */
+
+
+/**
+ * @class
+ * 
+ * @requires OpenLayers/Control.js
+ */
+OpenLayers.Control.MouseDefaults = OpenLayers.Class.create();
+OpenLayers.Control.MouseDefaults.prototype = 
+  OpenLayers.Class.inherit( OpenLayers.Control, {
+
+    /** @type Boolean */
+    performedDrag: false,
+
+    /** 
+     * @constructor
+     */
+    initialize: function() {
+        OpenLayers.Control.prototype.initialize.apply(this, arguments);
+
+        //register mousewheel events specifically on the window and document
+        OpenLayers.Event.observe(window, "DOMMouseScroll", 
+                      this.onWheelEvent.bindAsEventListener(this));
+        OpenLayers.Event.observe(window, "mousewheel", 
+                      this.onWheelEvent.bindAsEventListener(this));
+        OpenLayers.Event.observe(document, "mousewheel", 
+                      this.onWheelEvent.bindAsEventListener(this));
+    },
+
+    /**
+     * 
+     */    
+    draw: function() {
+        this.map.events.register( "click", this, this.defaultClick );
+        this.map.events.register( "dblclick", this, this.defaultDblClick );
+        this.map.events.register( "mousedown", this, this.defaultMouseDown );
+        this.map.events.register( "mouseup", this, this.defaultMouseUp );
+        this.map.events.register( "mousemove", this, this.defaultMouseMove );
+        this.map.events.register( "mouseout", this, this.defaultMouseOut );
+    },
+
+    /**
+     * @param {Event} evt
+     * 
+     * @type Boolean
+     */
+    defaultClick: function (evt) {
+        if (!OpenLayers.Event.isLeftClick(evt)) return;
+        var notAfterDrag = !this.performedDrag;
+        this.performedDrag = false;
+        return notAfterDrag;
+    },
+
+    /**
+    * @param {Event} evt
+    */
+    defaultDblClick: function (evt) {
+        var newCenter = this.map.getLonLatFromViewPortPx( evt.xy ); 
+        this.map.setCenter(newCenter, this.map.zoom + 1);
+        OpenLayers.Event.stop(evt);
+        return false;
+    },
+
+    /**
+    * @param {Event} evt
+    */
+    defaultMouseDown: function (evt) {
+        if (!OpenLayers.Event.isLeftClick(evt)) return;
+        this.mouseDragStart = evt.xy.clone();
+        this.performedDrag  = false;
+        if (evt.shiftKey) {
+            this.map.div.style.cursor = "crosshair";
+            this.zoomBox = OpenLayers.Util.createDiv('zoomBox',
+                                                     this.mouseDragStart,
+                                                     null,
+                                                     null,
+                                                     "absolute",
+                                                     "2px solid red");
+            this.zoomBox.style.backgroundColor = "white";
+            this.zoomBox.style.filter = "alpha(opacity=50)"; // IE
+            this.zoomBox.style.opacity = "0.50";
+            this.zoomBox.style.fontSize = "1px";
+            this.zoomBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
+            this.map.viewPortDiv.appendChild(this.zoomBox);
+        }
+        document.onselectstart=function() { return false; }
+        OpenLayers.Event.stop(evt);
+    },
+
+    /**
+    * @param {Event} evt
+    */
+    defaultMouseMove: function (evt) {
+        if (this.mouseDragStart != null) {
+            if (this.zoomBox) {
+                var deltaX = Math.abs(this.mouseDragStart.x - evt.xy.x);
+                var deltaY = Math.abs(this.mouseDragStart.y - evt.xy.y);
+                this.zoomBox.style.width = Math.max(1, deltaX) + "px";
+                this.zoomBox.style.height = Math.max(1, deltaY) + "px";
+                if (evt.xy.x < this.mouseDragStart.x) {
+                    this.zoomBox.style.left = evt.xy.x+"px";
+                }
+                if (evt.xy.y < this.mouseDragStart.y) {
+                    this.zoomBox.style.top = evt.xy.y+"px";
+                }
+            } else {
+                var deltaX = this.mouseDragStart.x - evt.xy.x;
+                var deltaY = this.mouseDragStart.y - evt.xy.y;
+                var size = this.map.getSize();
+                var newXY = new OpenLayers.Pixel(size.w / 2 + deltaX,
+                                                 size.h / 2 + deltaY);
+                var newCenter = this.map.getLonLatFromViewPortPx( newXY ); 
+                this.map.setCenter(newCenter, null, true);
+                this.mouseDragStart = evt.xy.clone();
+                this.map.div.style.cursor = "move";
+            }
+            this.performedDrag = true;
+        }
+    },
+
+    /**
+    * @param {Event} evt
+    */
+    defaultMouseUp: function (evt) {
+        if (!OpenLayers.Event.isLeftClick(evt)) return;
+        if (this.zoomBox) {
+            this.zoomBoxEnd(evt);    
+        } else {
+            if (this.performedDrag) {
+                this.map.setCenter(this.map.center);
+            }
+        }
+        document.onselectstart=null;
+        this.mouseDragStart = null;
+        this.map.div.style.cursor = "default";
+    },
+
+    /**
+    * @param {Event} evt
+    */
+    defaultMouseOut: function (evt) {
+        if (this.mouseDragStart != null && 
+            OpenLayers.Util.mouseLeft(evt, this.map.div)) {
+            if (this.zoomBox) {
+                this.removeZoomBox();
+            }
+            this.mouseDragStart = null;
+        }
+    },
+
+
+    /** User spun scroll wheel up
+     * 
+     */
+    defaultWheelUp: function()  {
+        this.map.zoomIn();
+    },
+
+    /** User spun scroll wheel down
+     * 
+     */
+    defaultWheelDown: function()  {
+        this.map.zoomOut();
+    },
+
+    /** Zoombox function. 
+     *
+     */
+    zoomBoxEnd: function(evt) {
+        if (this.mouseDragStart != null) {
+            if (Math.abs(this.mouseDragStart.x - evt.xy.x) > 5 ||    
+                Math.abs(this.mouseDragStart.y - evt.xy.y) > 5) {   
+                var start = this.map.getLonLatFromViewPortPx( this.mouseDragStart ); 
+                var end = this.map.getLonLatFromViewPortPx( evt.xy );
+                var top = Math.max(start.lat, end.lat);
+                var bottom = Math.min(start.lat, end.lat);
+                var left = Math.min(start.lon, end.lon);
+                var right = Math.max(start.lon, end.lon);
+                var bounds = new OpenLayers.Bounds(left, bottom, right, top);
+                this.map.zoomToExtent(bounds);
+            } else {
+                var end = this.map.getLonLatFromViewPortPx( evt.xy );
+                this.map.setCenter(new OpenLayers.LonLat(
+                  (end.lon),
+                  (end.lat)
+                 ), this.map.getZoom() + 1);
+            }    
+            this.removeZoomBox();
+       }
+    },
+
+    /**
+     * Remove the zoombox from the screen and nullify our reference to it.
+     */
+    removeZoomBox: function() {
+        this.map.viewPortDiv.removeChild(this.zoomBox);
+        this.zoomBox = null;
+    },
+
+
+/**
+ *  Mouse ScrollWheel code thanks to http://adomas.org/javascript-mouse-wheel/
+ */
+
+
+    /** Catch the wheel event and handle it xbrowserly
+     * 
+     * @param {Event} e
+     */
+    onWheelEvent: function(e){
+    
+        // first determine whether or not the wheeling was inside the map
+        var inMap = false;
+        var elem = OpenLayers.Event.element(e);
+        while(elem != null) {
+            if (this.map && elem == this.map.div) {
+                inMap = true;
+                break;
+            }
+            elem = elem.parentNode;
+        }
+        
+        if (inMap) {
+            
+            var delta = 0;
+            if (!e) {
+                e = window.event;
+            }
+            if (e.wheelDelta) {
+                delta = e.wheelDelta/120; 
+                if (window.opera) {
+                    delta = -delta;
+                }
+            } else if (e.detail) {
+                delta = -e.detail / 3;
+            }
+            if (delta) {
+                if (delta < 0) {
+                   this.defaultWheelDown();
+                } else {
+                   this.defaultWheelUp();
+                }
+            }
+            
+            //only wheel the map, not the window
+            OpenLayers.Event.stop(e);
+        }
+    },
+    
+    
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.Control.MouseDefaults"
+});
+


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/MouseDefaults.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/MousePosition.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/MousePosition.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/MousePosition.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,107 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
+ * for the full text of the license. */
+
+
+/**
+ * @class
+ * 
+ * @requires OpenLayers/Control.js
+ */
+OpenLayers.Control.MousePosition = OpenLayers.Class.create();
+OpenLayers.Control.MousePosition.prototype = 
+  OpenLayers.Class.inherit( OpenLayers.Control, {
+    
+    /** @type DOMElement */
+    element: null,
+    
+    /** @type String */
+    prefix: '',
+    
+    /** @type String */
+    separator: ', ',
+    
+    /** @type String */
+    suffix: '',
+    
+    /** @type int */
+    numdigits: 5,
+    
+    /** @type int */
+    granularity: 10,
+    
+    /** @type OpenLayers.LonLat */
+    lastXy: null,
+    
+    /**
+     * @constructor
+     * 
+     * @param {DOMElement} options Options for control.
+     */
+    initialize: function(options) {
+        OpenLayers.Control.prototype.initialize.apply(this, arguments);
+    },
+
+    /**
+     * @type DOMElement
+     */    
+    draw: function() {
+        OpenLayers.Control.prototype.draw.apply(this, arguments);
+
+        if (!this.element) {
+            this.div.left = "";
+            this.div.top = "";
+            this.div.className = "olControlMousePosition";
+            this.element = this.div;
+        }
+        
+        this.redraw();
+        return this.div;
+    },
+   
+    /**
+     * 
+     */
+    redraw: function(evt) {
+
+        var lonLat;
+
+        if (evt == null) {
+            lonLat = new OpenLayers.LonLat(0, 0);
+        } else {
+            if (this.lastXy == null ||
+                Math.abs(evt.xy.x - this.lastXy.x) > this.granularity ||
+                Math.abs(evt.xy.y - this.lastXy.y) > this.granularity)
+            {
+                this.lastXy = evt.xy;
+                return;
+            }
+
+            lonLat = this.map.getLonLatFromPixel(evt.xy);
+            this.lastXy = evt.xy;
+        }
+        
+        var digits = parseInt(this.numdigits);
+        var newHtml =
+            this.prefix +
+            lonLat.lon.toFixed(digits) +
+            this.separator + 
+            lonLat.lat.toFixed(digits) +
+            this.suffix;
+
+        if (newHtml != this.element.innerHTML) {
+            this.element.innerHTML = newHtml;
+        }
+    },
+
+    /** 
+     *
+     */
+    setMap: function() {
+        OpenLayers.Control.prototype.setMap.apply(this, arguments);
+        this.map.events.register( 'mousemove', this, this.redraw);
+    },     
+    
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.Control.MousePosition"
+});


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/MousePosition.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/MouseToolbar.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/MouseToolbar.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/MouseToolbar.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,310 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
+ * for the full text of the license. */
+
+
+/**
+ * @class
+ * 
+ * @requires OpenLayers/Control.js
+ * @requires OpenLayers/Control/MouseDefaults.js
+ */
+OpenLayers.Control.MouseToolbar = OpenLayers.Class.create();
+OpenLayers.Control.MouseToolbar.X = 6;
+OpenLayers.Control.MouseToolbar.Y = 300;
+OpenLayers.Control.MouseToolbar.prototype = 
+  OpenLayers.Class.inherit( OpenLayers.Control.MouseDefaults, {
+    
+    mode: null,
+
+    buttons: null,
+
+    direction: "vertical",
+    
+    /** @type String */
+    buttonClicked: null,
+    
+    initialize: function(position, direction) {
+        OpenLayers.Control.prototype.initialize.apply(this, arguments);
+        this.position = new OpenLayers.Pixel(OpenLayers.Control.MouseToolbar.X,
+                                             OpenLayers.Control.MouseToolbar.Y);
+        if (position) {
+            this.position = position;
+        }
+        if (direction) {
+            this.direction = direction; 
+        }
+        this.measureDivs = [];
+    },
+    
+    draw: function() {
+        OpenLayers.Control.prototype.draw.apply(this, arguments); 
+        OpenLayers.Control.MouseDefaults.prototype.draw.apply(this, arguments);
+        this.buttons = new Object();
+        var sz = new OpenLayers.Size(28,28);
+        var centered = this.position;
+        this._addButton("zoombox", "drag-rectangle-off.png", "drag-rectangle-on.png", centered, sz, "Shift->Drag to zoom to area");
+        centered = centered.add((this.direction == "vertical" ? 0 : sz.w), (this.direction == "vertical" ? sz.h : 0));
+        this._addButton("pan", "panning-hand-off.png", "panning-hand-on.png", centered, sz, "Drag the map to pan.");
+        centered = centered.add((this.direction == "vertical" ? 0 : sz.w), (this.direction == "vertical" ? sz.h : 0));
+        this.switchModeTo("pan");
+        return this.div;
+        
+    },
+    
+    _addButton:function(id, img, activeImg, xy, sz, title) {
+        var imgLocation = OpenLayers.Util.getImagesLocation() + img;
+        var activeImgLocation = OpenLayers.Util.getImagesLocation() + activeImg;
+        // var btn = new ol.AlphaImage("_"+id, imgLocation, xy, sz);
+        var btn = OpenLayers.Util.createAlphaImageDiv(
+                                    "OpenLayers_Control_MouseToolbar_" + id, 
+                                    xy, sz, imgLocation, "absolute");
+
+        //we want to add the outer div
+        this.div.appendChild(btn);
+        btn.imgLocation = imgLocation;
+        btn.activeImgLocation = activeImgLocation;
+        
+        btn.events = new OpenLayers.Events(this, btn, null, true);
+        btn.events.register("mousedown", this, this.buttonDown); 
+        btn.events.register("mouseup", this, this.buttonUp); 
+        btn.events.register("dblclick", this, OpenLayers.Event.stop);
+        btn.action = id;
+        btn.title = title;
+        btn.alt = title;
+        btn.map = this.map;
+
+        //we want to remember/reference the outer div
+        this.buttons[id] = btn;
+        return btn;
+    },
+
+    /**
+     * @param {Event} evt
+     */
+    buttonDown: function(evt) {
+        if (!OpenLayers.Event.isLeftClick(evt)) return;
+        this.buttonClicked = evt.element.action;
+        OpenLayers.Event.stop(evt);
+    },
+
+    /**
+     * @param {Event} evt
+     */
+    buttonUp: function(evt) {
+        if (!OpenLayers.Event.isLeftClick(evt)) return;
+        if (this.buttonClicked != null) {
+            if (this.buttonClicked == evt.element.action) {
+                this.switchModeTo(evt.element.action);
+            }
+            OpenLayers.Event.stop(evt);
+            this.buttonClicked = null;
+        }
+    },
+    
+    /**
+    * @param {Event} evt
+    */
+    defaultDblClick: function (evt) {
+        this.switchModeTo("pan");
+        this.performedDrag = false;
+        var newCenter = this.map.getLonLatFromViewPortPx( evt.xy ); 
+        this.map.setCenter(newCenter, this.map.zoom + 1);
+        OpenLayers.Event.stop(evt);
+        return false;
+    },
+
+    /**
+    * @param {Event} evt
+    */
+    defaultMouseDown: function (evt) {
+        if (!OpenLayers.Event.isLeftClick(evt)) return;
+        this.mouseDragStart = evt.xy.clone();
+        this.performedDrag = false;
+        this.startViaKeyboard = false;
+        if (evt.shiftKey && this.mode !="zoombox") {
+            this.switchModeTo("zoombox");
+            this.startViaKeyboard = true;
+        } else if (evt.altKey && this.mode !="measure") {
+            this.switchModeTo("measure");
+        } else if (!this.mode) {
+            this.switchModeTo("pan");
+        }
+        
+        switch (this.mode) {
+            case "zoombox":
+                this.map.div.style.cursor = "crosshair";
+                this.zoomBox = OpenLayers.Util.createDiv('zoomBox',
+                                                         this.mouseDragStart,
+                                                         null,
+                                                         null,
+                                                         "absolute",
+                                                         "2px solid red");
+                this.zoomBox.style.backgroundColor = "white";
+                this.zoomBox.style.filter = "alpha(opacity=50)"; // IE
+                this.zoomBox.style.opacity = "0.50";
+                this.zoomBox.style.fontSize = "1px";
+                this.zoomBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
+                this.map.viewPortDiv.appendChild(this.zoomBox);
+                this.performedDrag = true;
+                break;
+            case "measure":
+                var distance = "";
+                if (this.measureStart) {
+                    measureEnd = this.map.getLonLatFromViewPortPx(this.mouseDragStart);
+                    distance = OpenLayers.Util.distVincenty(this.measureStart, measureEnd);
+                    distance = Math.round(distance * 100) / 100;
+                    distance = distance + "km";
+                    this.measureStartBox = this.measureBox;
+                }    
+                this.measureStart = this.map.getLonLatFromViewPortPx(this.mouseDragStart);;
+                this.measureBox = OpenLayers.Util.createDiv(null,
+                                                         this.mouseDragStart.add(
+                                                           -2-parseInt(this.map.layerContainerDiv.style.left),
+                                                           -2-parseInt(this.map.layerContainerDiv.style.top)),
+                                                         null,
+                                                         null,
+                                                         "absolute");
+                this.measureBox.style.width="4px";
+                this.measureBox.style.height="4px";
+                this.measureBox.style.fontSize = "1px";
+                this.measureBox.style.backgroundColor="red";
+                this.measureBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
+                this.map.layerContainerDiv.appendChild(this.measureBox);
+                if (distance) {
+                    this.measureBoxDistance = OpenLayers.Util.createDiv(null,
+                                                         this.mouseDragStart.add(
+                                                           -2-parseInt(this.map.layerContainerDiv.style.left),
+                                                           2-parseInt(this.map.layerContainerDiv.style.top)),
+                                                         null,
+                                                         null,
+                                                         "absolute");
+                    
+                    this.measureBoxDistance.innerHTML = distance;
+                    this.measureBoxDistance.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
+                    this.map.layerContainerDiv.appendChild(this.measureBoxDistance);
+                    this.measureDivs.push(this.measureBoxDistance);
+                }
+                this.measureBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
+                this.map.layerContainerDiv.appendChild(this.measureBox);
+                this.measureDivs.push(this.measureBox);
+                break;
+            default:
+                this.map.div.style.cursor = "move";
+                break;
+        }
+        document.onselectstart = function() { return false; } 
+        OpenLayers.Event.stop(evt);
+    },
+
+    switchModeTo: function(mode) {
+        if (mode != this.mode) {
+            
+
+            if (this.mode && this.buttons[this.mode]) {
+                OpenLayers.Util.modifyAlphaImageDiv(this.buttons[this.mode], null, null, null, this.buttons[this.mode].imgLocation);
+            }
+            if (this.mode == "measure" && mode != "measure") {
+                for(var i = 0; i < this.measureDivs.length; i++) {
+                    if (this.measureDivs[i]) { 
+                        this.map.layerContainerDiv.removeChild(this.measureDivs[i]);
+                    }
+                }
+                this.measureDivs = [];
+                this.measureStart = null;
+            }
+            this.mode = mode;
+            if (this.buttons[mode]) {
+                OpenLayers.Util.modifyAlphaImageDiv(this.buttons[mode], null, null, null, this.buttons[mode].activeImgLocation);
+            }
+            switch (this.mode) {
+                case "zoombox":
+                    this.map.div.style.cursor = "crosshair";
+                    break;
+                default:
+                    this.map.div.style.cursor = "default";
+                    break;
+            }
+
+        } 
+    }, 
+
+    leaveMode: function() {
+        this.switchModeTo("pan");
+    },
+    
+    /**
+    * @param {Event} evt
+    */
+    defaultMouseMove: function (evt) {
+        if (this.mouseDragStart != null) {
+            switch (this.mode) {
+                case "zoombox": 
+                    var deltaX = Math.abs(this.mouseDragStart.x - evt.xy.x);
+                    var deltaY = Math.abs(this.mouseDragStart.y - evt.xy.y);
+                    this.zoomBox.style.width = Math.max(1, deltaX) + "px";
+                    this.zoomBox.style.height = Math.max(1, deltaY) + "px";
+                    if (evt.xy.x < this.mouseDragStart.x) {
+                        this.zoomBox.style.left = evt.xy.x+"px";
+                    }
+                    if (evt.xy.y < this.mouseDragStart.y) {
+                        this.zoomBox.style.top = evt.xy.y+"px";
+                    }
+                    break;
+                default:
+                    var deltaX = this.mouseDragStart.x - evt.xy.x;
+                    var deltaY = this.mouseDragStart.y - evt.xy.y;
+                    var size = this.map.getSize();
+                    var newXY = new OpenLayers.Pixel(size.w / 2 + deltaX,
+                                                     size.h / 2 + deltaY);
+                    var newCenter = this.map.getLonLatFromViewPortPx( newXY ); 
+                    this.map.setCenter(newCenter, null, true);
+                    this.mouseDragStart = evt.xy.clone();
+            }
+            this.performedDrag = true;
+        }
+    },
+
+    /**
+    * @param {Event} evt
+    */
+    defaultMouseUp: function (evt) {
+        if (!OpenLayers.Event.isLeftClick(evt)) return;
+        switch (this.mode) {
+            case "zoombox":
+                this.zoomBoxEnd(evt);
+                if (this.startViaKeyboard) this.leaveMode();
+                break;
+            case "pan":
+                if (this.performedDrag) {
+                    this.map.setCenter(this.map.center);
+                }        
+        }
+        document.onselectstart = null;
+        this.mouseDragStart = null;
+        this.map.div.style.cursor = "default";
+    },
+
+    /**
+    * @param {Event} evt
+    */
+    defaultMouseOut: function (evt) {
+        if (this.mouseDragStart != null
+            && OpenLayers.Util.mouseLeft(evt, this.map.div)) {
+            if (this.zoomBox) {
+                this.removeZoomBox();
+                if (this.startViaKeyboard) this.leaveMode();
+            }
+            this.mouseDragStart = null;
+            this.map.div.style.cursor = "default";
+        }
+    },
+
+    defaultClick: function (evt) {
+        if (this.performedDrag)  {
+            this.performedDrag = false;
+            return false;
+        }
+    }
+});
+


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/MouseToolbar.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/OverviewMap.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/OverviewMap.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/OverviewMap.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,514 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
+ * for the full text of the license. */
+/**
+ * @fileoverview Locator Map Control
+ * @author Tim Schaub
+ */
+
+// @require: OpenLayers/Control.js
+
+/** 
+* @class
+*/
+OpenLayers.Control.OverviewMap = OpenLayers.Class.create();
+
+OpenLayers.Control.OverviewMap.prototype = 
+  OpenLayers.Class.inherit( OpenLayers.Control, {
+
+    /** For div.id
+     * @type String */
+    id:  "OverviewMap",
+
+    /** @type DOMElement */
+    element: null,
+    
+    /**
+     * The overvew map itself.
+     * @type OpenLayers.Map
+     */
+    ovmap: null,
+        
+    /**
+     * Ordered list of layers in the overview map.  If none are sent at
+     * construction, then the default below is used.
+     * 
+     * @type Array(OpenLayers.Layer)
+     */
+    layers: [],
+
+    /**
+     * The ratio of the overview map resolution to the main map resolution
+     * at which to zoom farther out on the overview map.
+     * @type Float
+     */
+    minRatio: 8,
+
+    /**
+     * The ratio of the overview map resolution to the main map resolution
+     * at which to zoom farther in on the overview map.
+     * @type Float
+     */
+    maxRatio: 32,
+
+    /**
+     * @constructor
+     * @param {Object} options Hashtable of options to set on the overview map
+     */
+    initialize: function(options) {
+        OpenLayers.Control.prototype.initialize.apply(this, [options]);
+    },
+
+    /**
+     * @type DOMElement
+     */    
+    draw: function() {
+        OpenLayers.Control.prototype.draw.apply(this, arguments);
+
+        if(!(this.layers.length > 0)) {
+            if (this.map.baseLayer) {
+                var layer = this.map.baseLayer.clone();
+                this.layers = [layer];
+            } else {
+                this.map.events.register("changebaselayer", this, this.baseLayerDraw);
+                return this.div;
+            }
+        }
+
+        // create overview map DOM elements
+        this.element = document.createElement('div');
+        this.element.className = 'olControlOverviewMapElement';
+        this.element.style.display = 'none';
+
+        this.mapDiv = document.createElement('div');
+        this.mapDiv.style.width = '180px';
+        this.mapDiv.style.height = '90px';
+        this.mapDiv.style.position = 'relative';
+        this.mapDiv.style.overflow = 'hidden';
+        this.mapDiv.id = OpenLayers.Util.createUniqueID('overviewMap');
+        
+        this.extentRectangle = document.createElement('div');
+        this.extentRectangle.style.position = 'absolute';
+        this.extentRectangle.style.zIndex = 1000;  //HACK
+        this.extentRectangle.style.backgroundImage = 'url(' +
+                                        OpenLayers.Util.getImagesLocation() +
+                                        '/blank.png)';
+        this.extentRectangle.className = 'olControlOverviewMapExtentRectangle';
+        this.mapDiv.appendChild(this.extentRectangle);
+                
+        this.element.appendChild(this.mapDiv);  
+
+        this.div.appendChild(this.element);
+        this.div.className = 'olControlOverviewMapContainer';
+
+        this.map.events.register('moveend', this, this.update);
+        
+        // Set up events.  The image div recenters the map on click.
+        // The extent rectangle can be dragged to recenter the map.
+        // If the mousedown happened elsewhere, then mousemove and mouseup
+        // should slip through.
+        this.elementEvents = new OpenLayers.Events(this, this.element);
+        this.elementEvents.register('mousedown', this, function(e) {
+            OpenLayers.Event.stop(e);
+        });
+        this.elementEvents.register('click', this, function(e) {
+            OpenLayers.Event.stop(e);
+        });
+        this.elementEvents.register('dblclick', this, function(e) {
+            OpenLayers.Event.stop(e);
+        });
+        this.rectEvents = new OpenLayers.Events(this, this.extentRectangle);
+        this.rectEvents.register('mouseover', this, this.rectMouseOver);
+        this.rectEvents.register('mouseout', this, this.rectMouseOut);
+        this.rectEvents.register('mousedown', this, this.rectMouseDown);
+        this.rectEvents.register('mousemove', this, this.rectMouseMove);
+        this.rectEvents.register('mouseup', this, this.rectMouseUp);
+        this.rectEvents.register('click', this, function(e) {
+            OpenLayers.Event.stop(e);
+        });
+        this.rectEvents.register('dblclick', this, this.rectDblClick );
+        this.mapDivEvents = new OpenLayers.Events(this, this.mapDiv);
+        this.mapDivEvents.register('click', this, this.mapDivClick);
+
+        // There should be an option to place the control outside of the
+        // map viewport.  This would make these buttons optional.
+        var imgLocation = OpenLayers.Util.getImagesLocation();
+        // maximize button div
+        var img = imgLocation + 'layer-switcher-maximize.png';
+        this.maximizeDiv = OpenLayers.Util.createAlphaImageDiv(
+                                    'olControlOverviewMapMaximizeButton', 
+                                    null, 
+                                    new OpenLayers.Size(18,18), 
+                                    img, 
+                                    'absolute');
+        this.maximizeDiv.style.display = 'none';
+        this.maximizeDiv.className = 'olControlOverviewMapMaximizeButton';
+        OpenLayers.Event.observe(this.maximizeDiv, 
+                      'click', 
+                      this.maximizeControl.bindAsEventListener(this));
+        this.div.appendChild(this.maximizeDiv);
+
+        // minimize button div
+        var img = imgLocation + 'layer-switcher-minimize.png';
+        this.minimizeDiv = OpenLayers.Util.createAlphaImageDiv(
+                                    'OpenLayers_Control_minimizeDiv', 
+                                    null, 
+                                    new OpenLayers.Size(18,18), 
+                                    img, 
+                                    'absolute');
+        this.minimizeDiv.style.display = 'none';
+        this.minimizeDiv.className = 'olControlOverviewMapMinimizeButton';
+        OpenLayers.Event.observe(this.minimizeDiv, 
+                      'click', 
+                      this.minimizeControl.bindAsEventListener(this));
+
+        this.div.appendChild(this.minimizeDiv);
+        
+        this.minimizeControl();
+
+        return this.div;
+    },
+    
+    baseLayerDraw: function() {
+        this.draw();
+        this.map.events.unregister("changebaselayer", this, this.baseLayerDraw);
+    },
+
+    /**
+    * @param {OpenLayers.Event} evt
+    */
+    rectMouseOver: function (evt) {
+        this.extentRectangle.style.cursor = 'move';
+    },
+
+    /**
+    * @param {OpenLayers.Event} evt
+    */
+    rectMouseOut: function (evt) {
+        this.extentRectangle.style.cursor = 'default';
+        if(this.rectDragStart != null) {
+            if(this.performedRectDrag) {
+                this.updateMapToRect();
+            }        
+            document.onselectstart = null;
+            this.rectDragStart = null;
+        }
+    },
+
+    /**
+    * @param {OpenLayers.Event} evt
+    */
+    rectMouseDown: function (evt) {
+        if(!OpenLayers.Event.isLeftClick(evt)) return;
+        this.rectDragStart = evt.xy.clone();
+        this.performedRectDrag = false;
+        OpenLayers.Event.stop(evt);
+    },
+
+    /**
+    * @param {OpenLayers.Event} evt
+    */
+    rectMouseMove: function(evt) {
+        if(this.rectDragStart != null) {
+            var deltaX = this.rectDragStart.x - evt.xy.x;
+            var deltaY = this.rectDragStart.y - evt.xy.y;
+            var rectPxBounds = this.getRectPxBounds();
+            var rectTop = rectPxBounds.top;
+            var rectLeft = rectPxBounds.left;
+            var rectHeight = Math.abs(rectPxBounds.getHeight());
+            var rectWidth = rectPxBounds.getWidth();
+            // don't allow dragging off of parent element
+            var newTop = Math.max(0, (rectTop - deltaY));
+            newTop = Math.min(newTop,
+                              this.ovmap.size.h - this.hComp - rectHeight);
+            var newLeft = Math.max(0, (rectLeft - deltaX));
+            newLeft = Math.min(newLeft,
+                               this.ovmap.size.w - this.wComp - rectWidth);
+            this.setRectPxBounds(new OpenLayers.Bounds(newLeft,
+                                                       newTop + rectHeight,
+                                                       newLeft + rectWidth,
+                                                       newTop));
+            this.rectDragStart = evt.xy.clone();
+            this.performedRectDrag = true;
+            OpenLayers.Event.stop(evt);
+        }
+    },
+
+    /**
+    * @param {OpenLayers.Event} evt
+    */
+    rectMouseUp: function(evt) {
+        if(!OpenLayers.Event.isLeftClick(evt)) return;
+        if(this.performedRectDrag) {
+            this.updateMapToRect();
+            OpenLayers.Event.stop(evt);
+        }        
+        document.onselectstart = null;
+        this.rectDragStart = null;
+    },
+    
+    /**
+    * @param {OpenLayers.Event} evt
+    */
+    rectDblClick: function(evt) {
+        this.performedRectDrag = false;
+        OpenLayers.Event.stop(evt);
+        this.updateOverview();
+    },
+
+    /**
+    * @param {OpenLayers.Event} evt
+    */
+    mapDivClick: function(evt) {
+        var pxBounds = this.getRectPxBounds();
+        var pxCenter = pxBounds.getCenterPixel();
+        var deltaX = evt.xy.x - pxCenter.x;
+        var deltaY = evt.xy.y - pxCenter.y;
+        var top = pxBounds.top;
+        var left = pxBounds.left;
+        var height = Math.abs(pxBounds.getHeight());
+        var width = pxBounds.getWidth();
+        var newTop = Math.max(0, (top + deltaY));
+        newTop = Math.min(newTop, this.ovmap.size.h - height);
+        var newLeft = Math.max(0, (left + deltaX));
+        newLeft = Math.min(newLeft, this.ovmap.size.w - width);
+        this.setRectPxBounds(new OpenLayers.Bounds(newLeft,
+                                                   newTop + height,
+                                                   newLeft + width,
+                                                   newTop));
+        this.updateMapToRect();
+        OpenLayers.Event.stop(evt);
+    },
+
+    /** Set up the labels and divs for the control
+     * 
+     * @param {OpenLayers.Event} e
+     */
+    maximizeControl: function(e) {
+        this.element.style.display = '';
+        this.showToggle(false);
+        if (e != null) {
+            OpenLayers.Event.stop(e);                                            
+        }
+    },
+
+    /** Hide all the contents of the control, shrink the size, 
+     *   add the maximize icon
+     * 
+     * @param {OpenLayers.Event} e
+     */
+    minimizeControl: function(e) {
+        this.element.style.display = 'none';
+        this.showToggle(true);
+        if (e != null) {
+            OpenLayers.Event.stop(e);                                            
+        }
+    },
+
+    /** Hide/Show all LayerSwitcher controls depending on whether we are
+     *   minimized or not
+     * 
+     * @private
+     * 
+     * @param {Boolean} minimize
+     */
+    showToggle: function(minimize) {
+        this.maximizeDiv.style.display = minimize ? '' : 'none';
+        this.minimizeDiv.style.display = minimize ? 'none' : '';
+    },
+
+    /**
+     * Update the overview map after layers move.
+     */
+    update: function() {
+        if(this.ovmap == null) {
+            this.createMap();
+        }
+        
+        if(!this.isSuitableOverview()) {
+            this.updateOverview();
+        }
+        
+        // update extent rectangle
+        this.updateRectToMap();
+    },
+    
+    /**
+     * Determines if the overview map is suitable given the extent and
+     * resolution of the main map.
+     */
+    isSuitableOverview: function() {
+        var mapExtent = this.map.getExtent();
+        var maxExtent = this.map.maxExtent;
+        var testExtent = new OpenLayers.Bounds(
+                                Math.max(mapExtent.left, maxExtent.left),
+                                Math.max(mapExtent.bottom, maxExtent.bottom),
+                                Math.min(mapExtent.right, maxExtent.right),
+                                Math.min(mapExtent.top, maxExtent.top));        
+        var resRatio = this.ovmap.getResolution() / this.map.getResolution();
+        return ((resRatio > this.minRatio) &&
+                (resRatio <= this.maxRatio) &&
+                (this.ovmap.getExtent().containsBounds(testExtent)));
+    },
+    
+    updateOverview: function() {
+        var mapRes = this.map.getResolution();
+        var targetRes = this.ovmap.getResolution();
+        var resRatio = targetRes / mapRes;
+        if(resRatio > this.maxRatio) {
+            // zoom in overview map
+            targetRes = this.minRatio * mapRes;            
+        } else if(resRatio <= this.minRatio) {
+            // zoom out overview map
+            targetRes = this.maxRatio * mapRes;
+        }
+        this.ovmap.setCenter(this.map.center,
+                            this.ovmap.getZoomForResolution(targetRes));
+        this.updateRectToMap();
+    },
+    
+    createMap: function() {
+        // create the overview map
+        this.ovmap = new OpenLayers.Map(this.mapDiv.id, {controls: [], maxResolution: 'auto'});
+        this.ovmap.addLayers(this.layers);
+        this.ovmap.zoomToMaxExtent();
+        // check extent rectangle border width
+        this.wComp = parseInt(OpenLayers.Element.getStyle(this.extentRectangle,
+                                               'border-left-width')) +
+                     parseInt(OpenLayers.Element.getStyle(this.extentRectangle,
+                                               'border-right-width'));
+        this.wComp = (this.wComp) ? this.wComp : 2;
+        this.hComp = parseInt(OpenLayers.Element.getStyle(this.extentRectangle,
+                                               'border-top-width')) +
+                     parseInt(OpenLayers.Element.getStyle(this.extentRectangle,
+                                               'border-bottom-width'));
+        this.hComp = (this.hComp) ? this.hComp : 2;
+    },
+        
+    /**
+     * Updates the extent rectangle position and size to match the map extent
+     */
+    updateRectToMap: function() {
+        if(this.map.units != 'degrees') {
+            if(this.map.projection != this.ovmap.map.projection) {
+                alert('The overview map only works when it is in the same projection as the main map');
+            }
+        }
+        var pxBounds = this.getRectBoundsFromMapBounds(this.map.getExtent());
+        this.setRectPxBounds(pxBounds);
+    },
+    
+    /**
+     * Updates the map extent to match the extent rectangle position and size
+     */
+    updateMapToRect: function() {
+        var pxBounds = this.getRectPxBounds();
+        var lonLatBounds = this.getMapBoundsFromRectBounds(pxBounds);
+        this.map.setCenter(lonLatBounds.getCenterLonLat(), this.map.zoom);
+    },
+    
+    /**
+     * Get extent rectangle pixel bounds
+     * @returns An OpenLayers.Bounds wich is the extent rectangle's pixel
+     *          bounds (relative to the parent element)
+     */
+    getRectPxBounds: function() {
+        var top = parseInt(this.extentRectangle.style.top);
+        var left = parseInt(this.extentRectangle.style.left);
+        var height = parseInt(this.extentRectangle.style.height);
+        var width = parseInt(this.extentRectangle.style.width);
+        return new OpenLayers.Bounds(left, top + height, left + width, top);
+    },
+
+    /**
+     * Set extent rectangle pixel bounds.  
+     * @param {OpenLayers.Bounds} pxBounds
+     */
+    setRectPxBounds: function(pxBounds) {
+        var top = Math.max(pxBounds.top, 0);
+        var left = Math.max(pxBounds.left, 0);
+        var bottom = Math.min(pxBounds.top + Math.abs(pxBounds.getHeight()),
+                              this.ovmap.size.h - this.hComp);
+        var right = Math.min(pxBounds.left + pxBounds.getWidth(),
+                             this.ovmap.size.w - this.wComp);
+        this.extentRectangle.style.top = parseInt(top) + 'px';
+        this.extentRectangle.style.left = parseInt(left) + 'px';
+        this.extentRectangle.style.height = parseInt(bottom - top)+ 'px';
+        this.extentRectangle.style.width = parseInt(right - left) + 'px';
+    },
+
+    /**
+    * @param {OpenLayers.Bounds} lonLatBounds
+    *
+    * @returns An OpenLayers.Bounds which is the passed-in map lon/lat extent
+    *          translated into pixel bounds for the overview map
+    * @type OpenLayers.Bounds
+    */
+    getRectBoundsFromMapBounds: function(lonLatBounds) {
+        var leftBottomLonLat = new OpenLayers.LonLat(lonLatBounds.left,
+                                                     lonLatBounds.bottom);
+        var rightTopLonLat = new OpenLayers.LonLat(lonLatBounds.right,
+                                                   lonLatBounds.top);
+        var leftBottomPx = this.getOverviewPxFromLonLat(leftBottomLonLat);
+        var rightTopPx = this.getOverviewPxFromLonLat(rightTopLonLat);
+        return new OpenLayers.Bounds(leftBottomPx.x, leftBottomPx.y,
+                                     rightTopPx.x, rightTopPx.y);
+    },
+
+    /**
+    * @param {OpenLayers.Bounds} pxBounds
+    *
+    * @returns An OpenLayers.Bounds which is the passed-in overview rect bounds
+    *          translated into lon/lat bounds for the overview map
+    * @type OpenLayers.Bounds
+    */
+    getMapBoundsFromRectBounds: function(pxBounds) {
+        var leftBottomPx = new OpenLayers.Pixel(pxBounds.left,
+                                                pxBounds.bottom);
+        var rightTopPx = new OpenLayers.Pixel(pxBounds.right,
+                                              pxBounds.top);
+        var leftBottomLonLat = this.getLonLatFromOverviewPx(leftBottomPx);
+        var rightTopLonLat = this.getLonLatFromOverviewPx(rightTopPx);
+        return new OpenLayers.Bounds(leftBottomLonLat.lon, leftBottomLonLat.lat,
+                                     rightTopLonLat.lon, rightTopLonLat.lat);
+    },
+
+    /**
+    * @param {OpenLayers.Pixel} overviewMapPx
+    *
+    * @returns An OpenLayers.LonLat which is the passed-in overview map
+    *          OpenLayers.Pixel, translated into lon/lat by the overview map
+    * @type OpenLayers.LonLat
+    */
+    getLonLatFromOverviewPx: function(overviewMapPx) {
+        var size = this.ovmap.size;
+        var res  = this.ovmap.getResolution();
+        var center = this.ovmap.getExtent().getCenterLonLat();
+    
+        var delta_x = overviewMapPx.x - (size.w / 2);
+        var delta_y = overviewMapPx.y - (size.h / 2);
+        
+        return new OpenLayers.LonLat(center.lon + delta_x * res ,
+                                     center.lat - delta_y * res); 
+    },
+
+    /**
+    * @param {OpenLayers.LonLat} lonlat
+    *
+    * @returns An OpenLayers.Pixel which is the passed-in OpenLayers.LonLat, 
+    *          translated into overview map pixels
+    * @type OpenLayers.Pixel
+    */
+    getOverviewPxFromLonLat: function(lonlat) {
+        var res  = this.ovmap.getResolution();
+        var extent = this.ovmap.getExtent();
+        return new OpenLayers.Pixel(
+                       Math.round(1/res * (lonlat.lon - extent.left)),
+                       Math.round(1/res * (extent.top - lonlat.lat))
+                       );
+    },
+
+    /** @final @type String */
+    CLASS_NAME: 'OpenLayers.Control.OverviewMap'
+    
+});


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/OverviewMap.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/PanZoom.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/PanZoom.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/PanZoom.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,154 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
+ * for the full text of the license. */
+
+
+/**
+ * @class
+ * 
+ * @requires OpenLayers/Control.js
+ */
+OpenLayers.Control.PanZoom = OpenLayers.Class.create();
+OpenLayers.Control.PanZoom.X = 4;
+OpenLayers.Control.PanZoom.Y = 4;
+OpenLayers.Control.PanZoom.prototype = 
+  OpenLayers.Class.inherit( OpenLayers.Control, {
+
+    /** @type int */
+    slideFactor: 50,
+
+    /** @type Array of Button Divs */
+    buttons: null,
+
+    /** @type OpenLayers.Pixel */
+    position: null,
+
+    /**
+     * @constructor
+     */
+    initialize: function() {
+        OpenLayers.Control.prototype.initialize.apply(this, arguments);
+        this.position = new OpenLayers.Pixel(OpenLayers.Control.PanZoom.X,
+                                             OpenLayers.Control.PanZoom.Y);
+    },
+
+    /**
+    * @param {OpenLayers.Pixel} px
+    * 
+    * @returns A reference to the container div for the PanZoom control
+    * @type DOMElement
+    */
+    draw: function(px) {
+        // initialize our internal div
+        OpenLayers.Control.prototype.draw.apply(this, arguments);
+        px = this.position;
+
+        // place the controls
+        this.buttons = new Array();
+
+        var sz = new OpenLayers.Size(18,18);
+        var centered = new OpenLayers.Pixel(px.x+sz.w/2, px.y);
+
+        this._addButton("panup", "north-mini.png", centered, sz);
+        px.y = centered.y+sz.h;
+        this._addButton("panleft", "west-mini.png", px, sz);
+        this._addButton("panright", "east-mini.png", px.add(sz.w, 0), sz);
+        this._addButton("pandown", "south-mini.png", 
+                        centered.add(0, sz.h*2), sz);
+        this._addButton("zoomin", "zoom-plus-mini.png", 
+                        centered.add(0, sz.h*3+5), sz);
+        this._addButton("zoomworld", "zoom-world-mini.png", 
+                        centered.add(0, sz.h*4+5), sz);
+        this._addButton("zoomout", "zoom-minus-mini.png", 
+                        centered.add(0, sz.h*5+5), sz);
+        return this.div;
+    },
+    
+    /**
+     * @param {String} id
+     * @param {String} img
+     * @param {OpenLayers.Pixel} xy
+     * @param {OpenLayers.Size} sz
+     * 
+     * @returns A Div (an alphaImageDiv, to be precise) that contains the 
+     *          image of the button, and has all the proper event handlers
+     *          set.
+     * @type DOMElement
+     */
+    _addButton:function(id, img, xy, sz) {
+        var imgLocation = OpenLayers.Util.getImagesLocation() + img;
+        var btn = OpenLayers.Util.createAlphaImageDiv(
+                                    "OpenLayers_Control_PanZoom_" + id, 
+                                    xy, sz, imgLocation, "absolute");
+
+        //we want to add the outer div
+        this.div.appendChild(btn);
+
+        btn.onmousedown = this.buttonDown.bindAsEventListener(btn);
+        btn.ondblclick  = this.doubleClick.bindAsEventListener(btn);
+        btn.onclick  = this.doubleClick.bindAsEventListener(btn);
+        btn.action = id;
+        btn.map = this.map;
+        btn.slideFactor = this.slideFactor;
+
+        //we want to remember/reference the outer div
+        this.buttons.push(btn);
+        return btn;
+    },
+    
+    /**
+     * @param {Event} evt
+     * 
+     * @type Boolean
+     */
+    doubleClick: function (evt) {
+        OpenLayers.Event.stop(evt);
+        return false;
+    },
+    
+    /**
+     * @param {Event} evt
+     */
+    buttonDown: function (evt) {
+        if (!OpenLayers.Event.isLeftClick(evt)) return;
+
+        switch (this.action) {
+            case "panup": 
+                this.map.pan(0, -50);
+                break;
+            case "pandown": 
+                this.map.pan(0, 50);
+                break;
+            case "panleft": 
+                this.map.pan(-50, 0);
+                break;
+            case "panright": 
+                this.map.pan(50, 0);
+                break;
+            case "zoomin": 
+                this.map.zoomIn(); 
+                break;
+            case "zoomout": 
+                this.map.zoomOut(); 
+                break;
+            case "zoomworld": 
+                this.map.zoomToMaxExtent(); 
+                break;
+        }
+
+        OpenLayers.Event.stop(evt);
+    },
+
+    /**
+     * 
+     */
+    destroy: function() {
+        OpenLayers.Control.prototype.destroy.apply(this, arguments);
+        for(i=0; i<this.buttons.length; i++) {
+            this.buttons[i].map = null;
+        }
+    },
+    
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.Control.PanZoom"
+});


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/PanZoom.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/PanZoomBar.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/PanZoomBar.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/PanZoomBar.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,223 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
+ * for the full text of the license. */
+
+
+/**
+ * @class 
+ * 
+ * @requires OpenLayers/Control/PanZoom.js
+ */
+OpenLayers.Control.PanZoomBar = OpenLayers.Class.create();
+OpenLayers.Control.PanZoomBar.X = 4;
+OpenLayers.Control.PanZoomBar.Y = 4;
+OpenLayers.Control.PanZoomBar.prototype = 
+  OpenLayers.Class.inherit( OpenLayers.Control.PanZoom, {
+
+    /** @type Array(...) */
+    buttons: null,
+
+    /** @type int */
+    zoomStopWidth: 18,
+
+    /** @type int */
+    zoomStopHeight: 11,
+
+    initialize: function() {
+        OpenLayers.Control.PanZoom.prototype.initialize.apply(this, arguments);
+        this.position = new OpenLayers.Pixel(OpenLayers.Control.PanZoomBar.X,
+                                             OpenLayers.Control.PanZoomBar.Y);
+    },
+
+    /**
+     * @param {OpenLayers.Map} map
+     */
+    setMap: function(map) {
+        OpenLayers.Control.PanZoom.prototype.setMap.apply(this, arguments);
+        this.map.events.register("changebaselayer", this, this.redraw);
+    },
+
+    /** clear the div and start over.
+     * 
+     */
+    redraw: function() {
+        if (this.div != null) {
+            this.div.innerHTML = "";
+        }  
+        this.draw();
+    },
+    
+    /**
+    * @param {OpenLayers.Pixel} px
+    */
+    draw: function(px) {
+        // initialize our internal div
+        OpenLayers.Control.prototype.draw.apply(this, arguments);
+        px = this.position.clone();
+
+        // place the controls
+        this.buttons = new Array();
+
+        var sz = new OpenLayers.Size(18,18);
+        var centered = new OpenLayers.Pixel(px.x+sz.w/2, px.y);
+
+        this._addButton("panup", "north-mini.png", centered, sz);
+        px.y = centered.y+sz.h;
+        this._addButton("panleft", "west-mini.png", px, sz);
+        this._addButton("panright", "east-mini.png", px.add(sz.w, 0), sz);
+        this._addButton("pandown", "south-mini.png", centered.add(0, sz.h*2), sz);
+        this._addButton("zoomin", "zoom-plus-mini.png", centered.add(0, sz.h*3+5), sz);
+        centered = this._addZoomBar(centered.add(0, sz.h*4 + 5));
+        this._addButton("zoomout", "zoom-minus-mini.png", centered, sz);
+        return this.div;
+    },
+
+    /** 
+    * @param {OpenLayers.Pixel} location where zoombar drawing is to start.
+    */
+    _addZoomBar:function(centered) {
+        var imgLocation = OpenLayers.Util.getImagesLocation();
+        
+        var id = "OpenLayers_Control_PanZoomBar_Slider" + this.map.id;
+        var zoomsToEnd = this.map.getNumZoomLevels() - 1 - this.map.getZoom();
+        var slider = OpenLayers.Util.createAlphaImageDiv(id,
+                       centered.add(-1, zoomsToEnd * this.zoomStopHeight), 
+                       new OpenLayers.Size(20,9), 
+                       imgLocation+"slider.png",
+                       "absolute");
+        this.slider = slider;
+        
+        this.sliderEvents = new OpenLayers.Events(this, slider, null, true);
+        this.sliderEvents.register("mousedown", this, this.zoomBarDown);
+        this.sliderEvents.register("mousemove", this, this.zoomBarDrag);
+        this.sliderEvents.register("mouseup", this, this.zoomBarUp);
+        this.sliderEvents.register("dblclick", this, this.doubleClick);
+        this.sliderEvents.register("click", this, this.doubleClick);
+        
+        sz = new OpenLayers.Size();
+        sz.h = this.zoomStopHeight * this.map.getNumZoomLevels();
+        sz.w = this.zoomStopWidth;
+        var div = null
+        
+        if (OpenLayers.Util.alphaHack()) {
+            var id = "OpenLayers_Control_PanZoomBar" + this.map.id;
+            div = OpenLayers.Util.createAlphaImageDiv(id, centered,
+                                      new OpenLayers.Size(sz.w, 
+                                              this.zoomStopHeight),
+                                      imgLocation + "zoombar.png", 
+                                      "absolute", null, "crop");
+            div.style.height = sz.h;
+        } else {
+            div = OpenLayers.Util.createDiv(
+                        'OpenLayers_Control_PanZoomBar_Zoombar' + this.map.id,
+                        centered,
+                        sz,
+                        imgLocation+"zoombar.png");
+        }
+        
+        this.zoombarDiv = div;
+        
+        this.divEvents = new OpenLayers.Events(this, div, null, true);
+        this.divEvents.register("mousedown", this, this.divClick);
+        this.divEvents.register("mousemove", this, this.passEventToSlider);
+        this.divEvents.register("dblclick", this, this.doubleClick);
+        this.divEvents.register("click", this, this.doubleClick);
+        
+        this.div.appendChild(div);
+
+        this.startTop = parseInt(div.style.top);
+        this.div.appendChild(slider);
+
+        this.map.events.register("zoomend", this, this.moveZoomBar);
+
+        centered = centered.add(0, 
+            this.zoomStopHeight * this.map.getNumZoomLevels());
+        return centered; 
+    },
+    /* 
+     * @param evt
+     * This function is used to pass events that happen on the div, or the map,
+     * through to the slider, which then does its moving thing.
+     */
+    passEventToSlider:function(evt) {
+        this.sliderEvents.handleBrowserEvent(evt);
+    },
+    
+    /*
+     * divClick: Picks up on clicks directly on the zoombar div
+     *           and sets the zoom level appropriately.
+     */
+    divClick: function (evt) {
+        if (!OpenLayers.Event.isLeftClick(evt)) return;
+        var y = evt.xy.y;
+        var top = OpenLayers.Util.pagePosition(evt.object)[1];
+        var levels = Math.floor((y - top)/this.zoomStopHeight);
+        this.map.zoomTo((this.map.getNumZoomLevels() -1) -  levels);
+        OpenLayers.Event.stop(evt);
+    },
+    
+    /* 
+     * @param evt
+     * event listener for clicks on the slider
+     */
+    zoomBarDown:function(evt) {
+        if (!OpenLayers.Event.isLeftClick(evt)) return;
+        this.map.events.register("mousemove", this, this.passEventToSlider);
+        this.map.events.register("mouseup", this, this.passEventToSlider);
+        this.mouseDragStart = evt.xy.clone();
+        this.zoomStart = evt.xy.clone();
+        this.div.style.cursor = "move";
+        OpenLayers.Event.stop(evt);
+    },
+    
+    /*
+     * @param evt
+     * This is what happens when a click has occurred, and the client is dragging.
+     * Here we must ensure that the slider doesn't go beyond the bottom/top of the 
+     * zoombar div, as well as moving the slider to its new visual location
+     */
+    zoomBarDrag:function(evt) {
+        if (this.mouseDragStart != null) {
+            var deltaY = this.mouseDragStart.y - evt.xy.y
+            var offsets = OpenLayers.Util.pagePosition(this.zoombarDiv);
+            if ((evt.clientY - offsets[1]) > 0 && 
+                (evt.clientY - offsets[1]) < parseInt(this.zoombarDiv.style.height) - 2) {
+                var newTop = parseInt(this.slider.style.top) - deltaY;
+                this.slider.style.top = newTop+"px";
+            }
+            this.mouseDragStart = evt.xy.clone();
+            OpenLayers.Event.stop(evt);
+        }
+    },
+    
+    /* 
+     * @param evt
+     * Perform cleanup when a mouseup event is received -- discover new zoom level
+     * and switch to it.
+     */
+    zoomBarUp:function(evt) {
+        if (!OpenLayers.Event.isLeftClick(evt)) return;
+        if (this.zoomStart) {
+            this.div.style.cursor="default";
+            this.map.events.unregister("mouseup", this, this.passEventToSlider);
+            this.map.events.unregister("mousemove", this, this.passEventToSlider);
+            var deltaY = this.zoomStart.y - evt.xy.y
+            this.map.zoomTo(this.map.zoom + Math.round(deltaY/this.zoomStopHeight));
+            this.moveZoomBar();
+            this.mouseDragStart = null;
+            OpenLayers.Event.stop(evt);
+        }
+    },
+    
+    /* 
+    * Change the location of the slider to match the current zoom level.
+    */
+    moveZoomBar:function() {
+        var newTop = 
+            ((this.map.getNumZoomLevels()-1) - this.map.getZoom()) * 
+            this.zoomStopHeight + this.startTop + 1;
+        this.slider.style.top = newTop + "px";
+    },    
+    
+    CLASS_NAME: "OpenLayers.Control.PanZoomBar"
+});


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/PanZoomBar.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/Permalink.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/Permalink.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/Permalink.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,99 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
+ * for the full text of the license. */
+
+
+/**
+ * @class
+ * 
+ * @requires OpenLayers/Control.js
+ */
+OpenLayers.Control.Permalink = OpenLayers.Class.create();
+OpenLayers.Control.Permalink.prototype = 
+  OpenLayers.Class.inherit( OpenLayers.Control, {
+
+    /** @type DOMElement */
+    element: null,
+    
+    /** @type String */
+    base: '',
+
+    /**
+     * @constructor
+     * 
+     * @param {DOMElement} element
+     * @param {String} base
+     */
+    initialize: function(element, base) {
+        OpenLayers.Control.prototype.initialize.apply(this, arguments);
+        this.element = element;        
+        if (base) {
+            this.base = base;
+        }
+    },
+
+    /** Set the map property for the control. 
+     * 
+     * @param {OpenLayers.Map} map
+     */
+    setMap: function(map) {
+        OpenLayers.Control.prototype.setMap.apply(this, arguments);
+
+        //make sure we have an arg parser attached
+        for(var i=0; i< this.map.controls.length; i++) {
+            var control = this.map.controls[i];
+            if (control.CLASS_NAME == "OpenLayers.Control.ArgParser") {
+                break;
+            }
+        }
+        if (i == this.map.controls.length) {
+            this.map.addControl(new OpenLayers.Control.ArgParser());       
+        }
+
+    },
+
+    /**
+     * @type DOMElement
+     */    
+    draw: function() {
+        OpenLayers.Control.prototype.draw.apply(this, arguments);
+          
+        if (!this.element) {
+            this.div.className = 'olControlPermalink';
+            this.element = document.createElement("a");
+            this.element.style.fontSize="smaller";
+            this.element.innerHTML = "Permalink";
+            this.element.href="";
+            this.div.appendChild(this.element);
+        }
+        this.map.events.register('moveend', this, this.updateLink);
+        return this.div;
+    },
+   
+    /**
+     * 
+     */
+    updateLink: function() {
+        var center = this.map.getCenter();
+        var zoom = "zoom=" + this.map.getZoom(); 
+        var lat = "lat=" + Math.round(center.lat*100000)/100000;
+        var lon = "lon=" + Math.round(center.lon*100000)/100000;
+
+        var layers = "layers=";
+        for(var i=0; i< this.map.layers.length; i++) {
+            var layer = this.map.layers[i];
+
+            if (layer.isBaseLayer) {
+                layers += (layer == this.map.baseLayer) ? "B" : "0";
+            } else {
+                layers += (layer.getVisibility()) ? "T" : "F";           
+            }
+        }
+        var href = this.base + "?" + lat + "&" + lon + "&" + zoom + 
+                                   "&" + layers; 
+        this.element.href = href;
+    }, 
+
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.Control.Permalink"
+});


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/Permalink.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/Scale.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/Scale.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/Scale.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,64 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
+ * for the full text of the license. */
+
+
+/**
+ * @class
+ * 
+ * @requires OpenLayers/Control.js
+ */
+OpenLayers.Control.Scale = OpenLayers.Class.create();
+OpenLayers.Control.Scale.prototype = 
+  OpenLayers.Class.inherit( OpenLayers.Control, {
+    /** @type DOMElement */
+    element: null,
+    
+    /**
+     * @constructor
+     * 
+     * @param {DOMElement} element
+     * @param {String} base
+     */
+    initialize: function(element) {
+        OpenLayers.Control.prototype.initialize.apply(this, arguments);
+        this.element = element;        
+    },
+
+    /**
+     * @type DOMElement
+     */    
+    draw: function() {
+        OpenLayers.Control.prototype.draw.apply(this, arguments);
+        if (!this.element) {
+            this.element = document.createElement("div");
+            this.div.className = "olControlScale";
+            this.element.style.fontSize="smaller";
+            this.div.appendChild(this.element);
+        }
+        this.map.events.register( 'moveend', this, this.updateScale);
+        this.updateScale();
+        return this.div;
+    },
+   
+    /**
+     * 
+     */
+    updateScale: function() {
+        var scale = this.map.getScale();
+        if (!scale) return;
+
+        if (scale >= 9500 && scale <= 950000) {
+            scale = Math.round(scale / 1000) + "K";
+        } else if (scale >= 950000) {
+            scale = Math.round(scale / 1000000) + "M";
+        } else {
+            scale = Math.round(scale / 100) * 100;
+        }
+        this.element.innerHTML = "Scale = 1 : " + scale;
+    }, 
+    
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.Control.Scale"
+});
+


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control/Scale.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,88 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
+ * for the full text of the license. */
+
+
+/**
+* @class
+*/
+OpenLayers.Control = OpenLayers.Class.create();
+OpenLayers.Control.prototype = {
+
+    /** @type String */
+    id: null,
+    
+    /** this gets set in the addControl() function in OpenLayers.Map
+    * @type OpenLayers.Map */
+    map: null,
+
+    /** @type DOMElement */
+    div: null,
+
+    /** @type OpenLayers.Pixel */
+    position: null,
+
+    /** @type OpenLayers.Pixel */
+    mouseDragStart: null,
+
+    /**
+     * @constructor
+     * 
+     * @param {Object} options
+     */
+    initialize: function (options) {
+        OpenLayers.Util.extend(this, options);
+        
+        this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
+    },
+
+    /**
+     * 
+     */
+    destroy: function () {
+        // eliminate circular references
+        this.map = null;
+    },
+
+    /** Set the map property for the control. This is done through an accessor
+     *   so that subclasses can override this and take special action once 
+     *   they have their map variable set. 
+     * 
+     * @param {OpenLayers.Map} map
+     */
+    setMap: function(map) {
+        this.map = map;
+    },
+  
+    /**
+     * @param {OpenLayers.Pixel} px
+     *
+     * @returns A reference to the DIV DOMElement containing the control
+     * @type DOMElement
+     */
+    draw: function (px) {
+        if (this.div == null) {
+            this.div = OpenLayers.Util.createDiv();
+            this.div.id = this.id;
+            this.div.className = 'olControl';
+        }
+        if (px != null) {
+            this.position = px.clone();
+        }
+        this.moveTo(this.position);        
+        return this.div;
+    },
+
+    /**
+     * @param {OpenLayers.Pixel} px
+     */
+    moveTo: function (px) {
+        if ((px != null) && (this.div != null)) {
+            this.div.style.left = px.x + "px";
+            this.div.style.top = px.x + "px";
+        }
+    },
+
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.Control"
+};


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Control.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Events.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Events.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Events.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,338 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
+ * for the full text of the license. */
+
+
+OpenLayers.Event = {
+  KEY_BACKSPACE: 8,
+  KEY_TAB:       9,
+  KEY_RETURN:   13,
+  KEY_ESC:      27,
+  KEY_LEFT:     37,
+  KEY_UP:       38,
+  KEY_RIGHT:    39,
+  KEY_DOWN:     40,
+  KEY_DELETE:   46,
+
+  element: function(event) {
+    return event.target || event.srcElement;
+  },
+
+  isLeftClick: function(event) {
+    return (((event.which) && (event.which == 1)) ||
+            ((event.button) && (event.button == 1)));
+  },
+
+  pointerX: function(event) {
+    return event.pageX || (event.clientX +
+      (document.documentElement.scrollLeft || document.body.scrollLeft));
+  },
+
+  pointerY: function(event) {
+    return event.pageY || (event.clientY +
+      (document.documentElement.scrollTop || document.body.scrollTop));
+  },
+
+  stop: function(event) {
+    if (event.preventDefault) {
+      event.preventDefault();
+      event.stopPropagation();
+    } else {
+      event.returnValue = false;
+      event.cancelBubble = true;
+    }
+  },
+
+  // find the first node with the given tagName, starting from the
+  // node the event was triggered on; traverses the DOM upwards
+  findElement: function(event, tagName) {
+    var element = OpenLayers.Event.element(event);
+    while (element.parentNode && (!element.tagName ||
+        (element.tagName.toUpperCase() != tagName.toUpperCase())))
+      element = element.parentNode;
+    return element;
+  },
+
+  observers: false,
+
+  _observeAndCache: function(element, name, observer, useCapture) {
+    if (!this.observers) this.observers = [];
+    if (element.addEventListener) {
+      this.observers.push([element, name, observer, useCapture]);
+      element.addEventListener(name, observer, useCapture);
+    } else if (element.attachEvent) {
+      this.observers.push([element, name, observer, useCapture]);
+      element.attachEvent('on' + name, observer);
+    }
+  },
+
+  unloadCache: function() {
+    if (!OpenLayers.Event.observers) return;
+    for (var i = 0; i < OpenLayers.Event.observers.length; i++) {
+      OpenLayers.Event.stopObserving.apply(this, OpenLayers.Event.observers[i]);
+      OpenLayers.Event.observers[i][0] = null;
+    }
+    OpenLayers.Event.observers = false;
+  },
+
+  observe: function(elementParam, name, observer, useCapture) {
+    var element = $(elementParam);
+    useCapture = useCapture || false;
+
+    if (name == 'keypress' &&
+        (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
+        || element.attachEvent))
+      name = 'keydown';
+
+    this._observeAndCache(element, name, observer, useCapture);
+  },
+
+  stopObserving: function(elementParam, name, observer, useCapture) {
+    var element = $(elementParam);
+    useCapture = useCapture || false;
+
+    if (name == 'keypress' &&
+        (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
+        || element.detachEvent))
+      name = 'keydown';
+
+    if (element.removeEventListener) {
+      element.removeEventListener(name, observer, useCapture);
+    } else if (element.detachEvent) {
+      element.detachEvent('on' + name, observer);
+    }
+  }
+};
+/* prevent memory leaks in IE */
+OpenLayers.Event.observe(window, 'unload', OpenLayers.Event.unloadCache, false);
+
+if (window.Event) {
+  OpenLayers.Util.extend(window.Event, OpenLayers.Event);
+} else {
+  var Event = OpenLayers.Event;
+}
+
+
+
+/**
+ * @class
+ */
+OpenLayers.Events = OpenLayers.Class.create();
+OpenLayers.Events.prototype = {
+
+    /** @final @type Array: supported events */
+    BROWSER_EVENTS: [
+        "mouseover", "mouseout",
+        "mousedown", "mouseup", "mousemove", 
+        "click", "dblclick",
+        "resize", "focus", "blur"
+    ],
+
+    /** Hashtable of Array(Function): events listener functions 
+     * @type Object */
+    listeners: null,
+
+    /** @type Object: the code object issuing application events */
+    object: null,
+
+    /** @type DOMElement: the DOM element receiving browser events */
+    element: null,
+
+    /** @type Array: list of support application events */
+    eventTypes: null,
+
+    /**
+     * @constructor 
+     * 
+     * @param {OpenLayers.Map} object The js object to which this Events object
+     *                                is being added
+     * @param {DOMElement} element A dom element to respond to browser events
+     * @param {Array} eventTypes Array of custom application events
+     * @param {Boolean} fallThrough Allow events to fall through after these 
+     *                              have been handled?
+     */
+    initialize: function (object, element, eventTypes, fallThrough) {
+        this.object     = object;
+        this.element    = element;
+        this.eventTypes = eventTypes;
+        this.fallThrough = fallThrough;
+        this.listeners  = new Object();
+
+        // if eventTypes is specified, create a listeners list for each 
+        // custom application event.
+        if (this.eventTypes != null) 
+            for (var i = 0; i < this.eventTypes.length; i++)
+                this.listeners[ this.eventTypes[i] ] = new Array();
+
+        // if a dom element is specified, add a listeners list 
+        // for browser events on the element and register them
+        if (this.element != null)
+            this.attachToElement(element);
+    },
+
+    /**
+    * @param {HTMLDOMElement} element a DOM element to attach browser events to
+    */
+    attachToElement: function (element) {
+        for (var i = 0; i < this.BROWSER_EVENTS.length; i++) {
+            var eventType = this.BROWSER_EVENTS[i];
+
+            // every browser event has a corresponding application event 
+            // (whether it's listened for or not).
+            if (this.listeners[eventType] == null)
+                this.listeners[eventType] = new Array();
+
+            // use Prototype to register the event cross-browser
+            OpenLayers.Event.observe(element, eventType, 
+                this.handleBrowserEvent.bindAsEventListener(this));
+        }
+        // disable dragstart in IE so that mousedown/move/up works normally
+        OpenLayers.Event.observe(element, "dragstart", OpenLayers.Event.stop);
+    },
+
+    /**
+     * @param {String} type Name of the event to register
+     * @param {Object} obj The object to bind the context to for the callback#.
+     *                     If no object is specified, default is the Events's 
+     *                     'object' property.
+     * @param {Function} func The callback function. If no callback is 
+     *                        specified, this function does nothing.
+     * 
+     * #When the event is triggered, the 'func' function will be called, in the
+     *   context of 'obj'. Imagine we were to register an event, specifying an 
+     *   OpenLayers.Bounds Object as 'obj'. When the event is triggered, the 
+     *   context in the callback function will be our Bounds object. This means
+     *   that within our callback function, we can access the properties and 
+     *   methods of the Bounds object through the "this" variable. So our 
+     *   callback could execute something like: 
+     *   
+     *     leftStr = "Left: " + this.left;
+     *   
+     *                   or
+     *  
+     *     centerStr = "Center: " + this.getCenterLonLat();
+     * 
+     */
+    register: function (type, obj, func) {
+
+        if (func != null) {
+            if (obj == null)  {
+                obj = this.object;
+            }
+            var listeners = this.listeners[type];
+            if (listeners != null) {
+                listeners.push( {obj: obj, func: func} );
+            }
+        }
+    },
+    
+    /**
+     * @param {String} type
+     * @param {Object} obj If none specified, defaults to this.object
+     * @param {Function} func
+     */
+    unregister: function (type, obj, func) {
+        if (obj == null)  {
+            obj = this.object;
+        }
+        var listeners = this.listeners[type];
+        if (listeners != null) {
+            for (var i = 0; i < listeners.length; i++) {
+                if (listeners[i].obj == obj && listeners[i].func == func) {
+                    listeners.splice(i, 1);
+                    break;
+                }
+            }
+        }
+    },
+
+    /** Remove all listeners for a given event type. If type is not registered,
+     *   does nothing.
+     * 
+     * @param {String} type
+     */
+    remove: function(type) {
+        if (this.listeners[type] != null) {
+            this.listeners[type] = new Array();
+        }
+    },
+
+    /** Trigger a specified registered event
+     * 
+     * @param {String} type
+     * @param {Event} evt
+     */
+    triggerEvent: function (type, evt) {
+
+        // prep evt object with object & div references
+        if (evt == null) {
+            evt = new Object();
+        }
+        evt.object = this.object;
+        evt.element = this.element;
+
+        // execute all callbacks registered for specified type
+        var listeners = this.listeners[type];
+        if ((listeners != null) && (listeners.length > 0)) {
+            for (var i = 0; i < listeners.length; i++) {
+                var callback = listeners[i];
+                var continueChain;
+                if (callback.obj != null) {
+                    // use the 'call' method to bind the context to callback.obj
+                    continueChain = callback.func.call(callback.obj, evt);
+                } else {
+                    continueChain = callback.func(evt);
+                }
+    
+                if ((continueChain != null) && (continueChain == false)) {
+                    // if callback returns false, execute no more callbacks.
+                    break;
+                }
+            }
+            // don't fall through to other DOM elements
+            if (!this.fallThrough) {           
+                OpenLayers.Util.safeStopPropagation(evt);
+            }
+        }
+    },
+
+    /** Basically just a wrapper to the triggerEvent() function, but takes 
+     *   care to set a property 'xy' on the event with the current mouse 
+     *   position.
+     * 
+     * @private
+     * 
+     * @param {Event} evt
+     */
+    handleBrowserEvent: function (evt) {
+        evt.xy = this.getMousePosition(evt); 
+        this.triggerEvent(evt.type, evt)
+    },
+
+    /**
+     * @private 
+     * 
+     * @param {Event} evt
+     * 
+     * @returns The current xy coordinate of the mouse, adjusted for offsets
+     * @type OpenLayers.Pixel
+     */
+    getMousePosition: function (evt) {
+        if (!this.element.offsets) {
+            this.element.offsets = OpenLayers.Util.pagePosition(this.element);
+            this.element.offsets[0] += (document.documentElement.scrollLeft
+                         || document.body.scrollLeft);
+            this.element.offsets[1] += (document.documentElement.scrollTop
+                         || document.body.scrollTop);
+        }
+        return new OpenLayers.Pixel(
+            (evt.clientX + (document.documentElement.scrollLeft
+                         || document.body.scrollLeft)) - this.element.offsets[0], 
+            (evt.clientY + (document.documentElement.scrollTop
+                         || document.body.scrollTop)) - this.element.offsets[1] 
+        ); 
+    },
+
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.Events"
+};


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Events.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Feature/WFS.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Feature/WFS.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Feature/WFS.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,62 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
+ * for the full text of the license. */
+
+
+/**
+ * @class
+ * 
+ * @requires OpenLayers/Feature.js
+ */
+OpenLayers.Feature.WFS = OpenLayers.Class.create();
+OpenLayers.Feature.WFS.prototype = 
+  OpenLayers.Class.inherit( OpenLayers.Feature, {
+      
+    /** 
+     * @constructor
+     * 
+     * @param {OpenLayers.Layer} layer
+     * @param {XMLNode} xmlNode
+     */
+    initialize: function(layer, xmlNode) {
+        var newArguments = arguments;
+        var data = this.processXMLNode(xmlNode);
+        newArguments = new Array(layer, data.lonlat, data)
+        OpenLayers.Feature.prototype.initialize.apply(this, newArguments);
+        this.createMarker();
+        this.layer.addMarker(this.marker);
+    },
+    
+    destroy: function() {
+        if (this.marker != null) {
+            this.layer.removeMarker(this.marker);  
+        }
+        OpenLayers.Feature.prototype.destroy.apply(this, arguments);
+    },
+
+    /**
+     * @param {XMLNode} xmlNode
+     * 
+     * @returns Data Object with 'id', 'lonlat', and private properties set
+     * @type Object
+     */
+    processXMLNode: function(xmlNode) {
+        //this should be overridden by subclasses
+        // must return an Object with 'id' and 'lonlat' values set
+        var point = OpenLayers.Ajax.getElementsByTagNameNS(xmlNode, "http://www.opengis.net/gml", "gml", "Point");
+        var text  = OpenLayers.Util.getXmlNodeValue(OpenLayers.Ajax.getElementsByTagNameNS(point[0], "http://www.opengis.net/gml","gml", "coordinates")[0]);
+        var floats = text.split(",");
+        return {lonlat: new OpenLayers.LonLat(parseFloat(floats[0]),
+                                              parseFloat(floats[1])),
+                id: null};
+
+    },
+    
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.Feature.WFS"
+});
+  
+  
+  
+  
+


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Feature/WFS.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Feature.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Feature.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Feature.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,155 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
+ * for the full text of the license. */
+
+
+/**
+ * @class
+ * 
+ * @requires OpenLayers/Util.js
+ */
+OpenLayers.Feature = OpenLayers.Class.create();
+OpenLayers.Feature.prototype= {
+
+    /** @type OpenLayers.Events */
+    events:null,
+
+    /** @type OpenLayers.Layer */
+    layer: null,
+
+    /** @type String */
+    id: null,
+    
+    /** @type OpenLayers.LonLat */
+    lonlat:null,
+
+    /** @type Object */
+    data:null,
+
+    /** @type OpenLayers.Marker */
+    marker: null,
+
+    /** @type OpenLayers.Popup */
+    popup: null,
+
+    /** 
+     * @constructor
+     * 
+     * @param {OpenLayers.Layer} layer
+     * @param {OpenLayers.LonLat} lonlat
+     * @param {Object} data
+     */
+    initialize: function(layer, lonlat, data) {
+        this.layer = layer;
+        this.lonlat = lonlat;
+        this.data = (data != null) ? data : new Object();
+        this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_"); 
+    },
+
+    /**
+     * 
+     */
+    destroy: function() {
+
+        //remove the popup from the map
+        if ((this.layer != null) && (this.layer.map != null)) {
+            if (this.popup != null) {
+                this.layer.map.removePopup(this.popup);
+            }
+        }
+
+        this.events = null;
+        this.layer = null;
+        this.id = null;
+        this.lonlat = null;
+        this.data = null;
+        if (this.marker != null) {
+            this.destroyMarker(this.marker);
+            this.marker = null;
+        }
+        if (this.popup != null) {
+            this.destroyPopup(this.popup);
+            this.popup = null;
+        }
+    },
+    
+    /**
+     * @returns Whether or not the feature is currently visible on screen
+     *           (based on its 'lonlat' property)
+     * @type Boolean
+     */
+    onScreen:function() {
+        
+        var onScreen = false;
+        if ((this.layer != null) && (this.layer.map != null)) {
+            var screenBounds = this.layer.map.getExtent();
+            onScreen = screenBounds.containsLonLat(this.lonlat);
+        }    
+        return onScreen;
+    },
+    
+
+    /**
+     * @returns A Marker Object created from the 'lonlat' and 'icon' properties
+     *          set in this.data. If no 'lonlat' is set, returns null. If no
+     *          'icon' is set, OpenLayers.Marker() will load the default image.
+     *          
+     *          Note: this.marker is set to return value
+     * 
+     * @type OpenLayers.Marker
+     */
+    createMarker: function() {
+
+        var marker = null;
+        
+        if (this.lonlat != null) {
+            this.marker = new OpenLayers.Marker(this.lonlat, this.data.icon);
+        }
+        return this.marker;
+    },
+
+    /** If user overrides the createMarker() function, s/he should be able
+     *   to also specify an alternative function for destroying it
+     */
+    destroyMarker: function() {
+        this.marker.destroy();  
+    },
+
+    /**
+     * @returns A Popup Object created from the 'lonlat', 'popupSize',
+     *          and 'popupContentHTML' properties set in this.data. It uses
+     *          this.marker.icon as default anchor. 
+     *          
+     *          If no 'lonlat' is set, returns null. 
+     *          If no this.marker has been created, no anchor is sent.
+     * 
+     *          Note: this.popup is set to return value
+     * 
+     * @type OpenLayers.Popup.AnchoredBubble
+     */
+    createPopup: function() {
+
+        if (this.lonlat != null) {
+            
+            var id = this.id + "_popup";
+            var anchor = (this.marker) ? this.marker.icon : null;
+
+            this.popup = new OpenLayers.Popup.AnchoredBubble(id, 
+                                                    this.lonlat,
+                                                    this.data.popupSize,
+                                                    this.data.popupContentHTML,
+                                                    anchor); 
+        }        
+        return this.popup;
+    },
+
+    
+    /** As with the marker, if user overrides the createPopup() function, s/he 
+     *   should also be able to override the destruction
+     */
+    destroyPopup: function() {
+        this.popup.destroy() 
+    },
+    
+    CLASS_NAME: "OpenLayers.Feature"
+};


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Feature.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Icon.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Icon.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Icon.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,138 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
+ * for the full text of the license. */
+
+
+/**
+ * @class
+ */
+OpenLayers.Icon = OpenLayers.Class.create();
+OpenLayers.Icon.prototype = {
+    
+    /** image url
+    * @type String */
+    url: null,
+    
+    /** @type OpenLayers.Size */
+    size:null,
+
+    /** distance in pixels to offset the image when being rendered
+    * @type OpenLayers.Pixel */
+    offset: null,    
+    
+    /** Function to calculate the offset (based on the size) 
+     * @type OpenLayers.Pixel */
+    calculateOffset: null,    
+    
+    /** @type DOMElement */
+    imageDiv: null,
+
+    /** @type OpenLayers.Pixel */
+    px: null,
+    
+    /** 
+    * @constructor
+    *
+    * @param {String} url
+    * @param {OpenLayers.Size} size
+    * @param {Function} calculateOffset
+    */
+    initialize: function(url, size, offset, calculateOffset) {
+        this.url = url;
+        this.size = (size) ? size : new OpenLayers.Size(20,20);
+        this.offset = offset;
+        this.calculateOffset = (calculateOffset) ? calculateOffset
+                                 : function(size) {
+                                     return new OpenLayers.Pixel(-(size.w/2), 
+                                                                -(size.h/2));
+                                   };
+
+        this.imageDiv = OpenLayers.Util.createAlphaImageDiv();
+    },
+    
+    destroy: function() {
+        this.imageDiv = null;
+    },
+
+    /** 
+    * @returns A fresh copy of the icon.
+    * @type OpenLayers.Icon
+    */
+    clone: function() {
+        return new OpenLayers.Icon(this.url, 
+                                   this.size, 
+                                   this.offset, 
+                                   this.calculateOffset);
+    },
+    
+    /**
+     * @param {OpenLayers.Size} size
+     */
+    setSize: function(size) {
+        if (size != null) {
+            this.size = size;
+        }
+        this.draw();
+    },
+
+    /** 
+    * @param {OpenLayers.Pixel} px
+    * 
+    * @return A new DOM Image of this icon set at the location passed-in
+    * @type DOMElement
+    */
+    draw: function(px) {
+        OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, 
+                                            null, 
+                                            null, 
+                                            this.size, 
+                                            this.url, 
+                                            "absolute");
+        this.moveTo(px);
+        return this.imageDiv;
+    }, 
+
+    
+    /** Change the icon's opacity
+     * @param {float} opacity
+     */
+    setOpacity: function(opacity) {
+        OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, null, null, 
+                                            null, null, null, null, opacity);
+
+    },
+    
+    /**
+    * @param {OpenLayers.Pixel} px
+    */
+    moveTo: function (px) {
+        //if no px passed in, use stored location
+        if (px != null) {
+            this.px = px;
+        }
+
+        if (this.imageDiv != null) {
+            if (this.px == null) {
+                this.display(false);
+            } else {
+                if (this.calculateOffset) {
+                    this.offset = this.calculateOffset(this.size);  
+                }
+                var offsetPx = this.px.offset(this.offset);
+                OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, offsetPx);
+                this.display(true);
+            }
+        }
+    },
+    
+    /** Hide or show the icon
+     * 
+     * @param {Boolean} display
+     */
+    display: function(display) {
+        this.imageDiv.style.display = (display) ? "" : "none"; 
+    },
+    
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.Icon"
+};


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Icon.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/Boxes.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/Boxes.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/Boxes.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,49 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
+ * for the full text of the license. */
+
+
+/**
+ * @class
+ * 
+ * @requires OpenLayers/Layer.js
+ * @requires OpenLayers/Layer/Markers.js
+ */
+OpenLayers.Layer.Boxes = OpenLayers.Class.create();
+OpenLayers.Layer.Boxes.prototype = 
+    OpenLayers.Class.inherit( OpenLayers.Layer.Markers, {
+
+    initialize: function () {
+        OpenLayers.Layer.Markers.prototype.initialize.apply(this, arguments);
+    },
+    
+    /** Calculate the pixel location for the marker, create it, and
+    *    add it to the layer's div
+    *
+    * @private
+    *
+    * @param {OpenLayers.Marker.Box} marker
+    */
+    drawMarker: function(marker) {
+        var bounds   = marker.bounds;
+        var topleft  = this.map.getLayerPxFromLonLat(
+                            new OpenLayers.LonLat(bounds.left,  bounds.top));
+        var botright = this.map.getLayerPxFromLonLat(
+                             new OpenLayers.LonLat(bounds.right, bounds.bottom));
+        if (botright == null || topleft == null) {
+            marker.display(false);
+        } else {
+            var sz = new OpenLayers.Size(
+                Math.max(1, botright.x - topleft.x),
+                Math.max(1, botright.y - topleft.y));
+            var markerDiv = marker.draw(topleft, sz);
+            if (!marker.drawn) {
+                this.div.appendChild(markerDiv);
+                marker.drawn = true;
+            }
+        }
+    },
+
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.Layer.Boxes"
+});


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/Boxes.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/Canvas.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/Canvas.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/Canvas.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,120 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
+ * for the full text of the license. */
+
+
+/**
+ * @class
+ * 
+ * @requires OpenLayers/Layer.js
+ */
+OpenLayers.Layer.Canvas = OpenLayers.Class.create();
+OpenLayers.Layer.Canvas.prototype = 
+  OpenLayers.Class.inherit( OpenLayers.Layer, {
+    
+    /** Canvas layer is never a base layer. 
+     * 
+     * @type Boolean
+     */
+    isBaseLayer: false,
+    isFixed: true, 
+    /** internal marker list
+    * @type Array(OpenLayers.Marker) */
+    canvas: null,
+
+    lines: new Array(),
+    
+    /**
+    * @constructor
+    *
+    * @param {String} name
+    * @param {Object} options Hashtable of extra options to tag onto the layer
+    */
+    initialize: function(name, options) {
+        OpenLayers.Layer.prototype.initialize.apply(this, arguments);
+    },
+    
+    /**
+     * 
+     */
+    destroy: function() {
+        // xxx actually destroy the canvas to scavenge ram?
+        canvas = null;
+        OpenLayers.Layer.prototype.destroy.apply(this, arguments);
+    },
+
+    
+    /** 
+     * @param {OpenLayers.Bounds} bounds
+     * @param {Boolean} zoomChanged
+     * @param {Boolean} dragging
+     */
+    moveTo:function(bounds, zoomChanged, dragging) {
+        OpenLayers.Layer.prototype.moveTo.apply(this, arguments);
+
+        this.redraw();
+    },
+
+    setStrokeColor: function(color) {
+        var ctx = this.canvas.getContext("2d");
+        ctx.strokeStyle = color;
+    },
+    setStrokeWidth: function(width) {
+        var ctx = this.canvas.getContext("2d");
+        ctx.lineWidth = width;
+    },
+    setAlpha: function(alpha) {
+        var ctx = this.canvas.getContext("2d");
+        ctx.globalAlpha = alpha;
+    },
+    /**
+     * 
+     */
+    clearCanvas: function() {
+        if(this.canvas != null) {
+          this.canvas.getContext("2d").clearRect(0,0,this.map.getSize().w, this.map.getSize().h);
+          // xxx use real width and height
+        }
+    },
+
+    drawLine: function(start, end) {
+        var ctx = this.canvas.getContext("2d");
+        this.addLine(start, end);
+        this.lines.push(new Array(start,end, ctx.strokeStyle, ctx.lineWidth, ctx.globalAlpha));
+    },
+    addLine: function(start, end) {
+        var ctx = this.canvas.getContext("2d");
+        var startpx = this.map.getPixelFromLonLat(start);
+        var endpx = this.map.getPixelFromLonLat(end);
+        ctx.beginPath();
+        ctx.moveTo(startpx.x, startpx.y);
+        ctx.lineTo(endpx.x, endpx.y);
+        ctx.closePath();
+        ctx.stroke();
+    },
+    
+    /** clear all the marker div's from the layer and then redraw all of them.
+    *    Use the map to recalculate new placement of markers.
+    */
+    redraw: function() {
+        // xxx rebuild the canvas if smaller than the view
+        // xxx may wish to overside the canvas with overflow=hidden by default
+        if(!this.canvas) {
+          this.canvas = document.createElement("CANVAS");
+          this.canvas.setAttribute("width",this.map.getSize().w);
+          this.canvas.setAttribute("height",this.map.getSize().h);
+          this.div.appendChild(this.canvas);
+        } else {
+            this.clearCanvas();
+        }
+        for(var i=0; i < this.lines.length; i++) {
+            this.setStrokeColor(this.lines[i][2]);
+            this.setStrokeWidth(this.lines[i][3]);
+            this.setAlpha(this.lines[i][4]);
+            this.addLine(this.lines[i][0], this.lines[i][1]);
+        }    
+    },
+
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.Layer.Canvas"
+});


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/Canvas.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/EventPane.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/EventPane.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/EventPane.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,313 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
+ * for the full text of the license. */
+
+
+/**
+ * @class
+ * 
+ * @requires OpenLayers/Layer.js
+ * @requires OpenLayers/Util.js
+ */
+OpenLayers.Layer.EventPane = OpenLayers.Class.create();
+OpenLayers.Layer.EventPane.prototype = 
+  OpenLayers.Class.inherit( OpenLayers.Layer, {
+
+    /** EventPaned layers are always base layers, by necessity.
+     * 
+     * @type Boolean */
+    isBaseLayer: true,
+
+    /** EventPaned layers are fixed by default.
+     * 
+     * @type Boolean */
+    isFixed: true,
+
+    /** @type DOMElement */
+    pane: null,
+
+
+    /** This is the object which will be used to load the 3rd party library
+     *   in the case of the google layer, this will be of type GMap, 
+     *   in the case of the ve layer, this will be of type VEMap
+     * 
+     * @type Object */
+    mapObject: null,
+
+
+    /**
+     * @constructor
+     * 
+     * @param {String} name
+     * @param {Object} options Hashtable of extra options to tag onto the layer
+     */
+    initialize: function(name, options) {
+        OpenLayers.Layer.prototype.initialize.apply(this, arguments);
+        if (this.pane == null) {
+            this.pane = OpenLayers.Util.createDiv(this.div.id + "_EventPane");
+        }
+    },
+    
+    /**
+     * 
+     */
+    destroy: function() {
+        this.mapObject = null;
+        OpenLayers.Layer.prototype.destroy.apply(this, arguments); 
+    },
+
+    
+    /** Set the map property for the layer. This is done through an accessor
+     *   so that subclasses can override this and take special action once 
+     *   they have their map variable set. 
+     * 
+     * @param {OpenLayers.Map} map
+     */
+    setMap: function(map) {
+        OpenLayers.Layer.prototype.setMap.apply(this, arguments);
+        
+        this.pane.style.zIndex = parseInt(this.div.style.zIndex) + 1;
+        this.pane.style.display = this.div.style.display;
+        this.pane.style.width="100%";
+        this.pane.style.height="100%";
+        if (/MSIE/.test(navigator.userAgent)) {
+          this.pane.style.background = "url("+OpenLayers.Util.getImagesLocation()+"blank.gif)";
+        }
+
+        if (this.isFixed) {
+            this.map.viewPortDiv.appendChild(this.pane);
+        } else {
+            this.map.layerContainerDiv.appendChild(this.pane);
+        }
+
+        // once our layer has been added to the map, we can load it
+        this.loadMapObject();
+    
+        // if map didn't load, display warning
+        if (this.mapObject == null) {
+            this.loadWarningMessage();
+        }
+    },
+  
+    /** If we can't load the GMap, then display an error message to the 
+     *   user and tell them where to go for help.
+     * 
+     * @private
+     * 
+     */
+    loadWarningMessage:function() {
+
+        this.div.style.backgroundColor = "darkblue";
+
+        var viewSize = this.map.getSize();
+        
+        msgW = Math.min(viewSize.w, 300);
+        msgH = Math.min(viewSize.h, 200);
+        var size = new OpenLayers.Size(msgW, msgH);
+
+        var centerPx = new OpenLayers.Pixel(viewSize.w/2, viewSize.h/2);
+
+        var topLeft = centerPx.add(-size.w/2, -size.h/2);            
+
+        var div = OpenLayers.Util.createDiv(this.name + "_warning", 
+                                            topLeft, 
+                                            size,
+                                            null,
+                                            null,
+                                            null,
+                                            "auto");
+
+        div.style.padding = "7px";
+        div.style.backgroundColor = "yellow";
+
+        div.innerHTML = this.getWarningHTML();
+        this.div.appendChild(div);
+    },
+  
+  
+    /** 
+     * @param {Boolean} display
+     */
+    display: function(display) {
+        OpenLayers.Layer.prototype.display.apply(this, arguments);
+        this.pane.style.display = this.div.style.display;
+    },
+  
+    /**
+     * @param {int} zIndex
+     */
+    setZIndex: function (zIndex) {
+        OpenLayers.Layer.prototype.setZIndex.apply(this, arguments);
+        this.pane.style.zIndex = parseInt(this.div.style.zIndex) + 1;
+    },
+
+    /** 
+     * @param {OpenLayers.Bounds} bounds
+     * @param {Boolean} zoomChanged
+     * @param {Boolean} dragging
+     */
+    moveTo:function(bounds, zoomChanged, dragging) {
+        OpenLayers.Layer.prototype.moveTo.apply(this, arguments);
+
+        if (this.mapObject != null) {
+
+            var newCenter = this.map.getCenter();
+            var newZoom = this.map.getZoom();
+
+            if (newCenter != null) {
+
+                var moOldCenter = this.getMapObjectCenter();
+                var oldCenter = this.getOLLonLatFromMapObjectLonLat(moOldCenter);
+
+                var moOldZoom = this.getMapObjectZoom();
+                var oldZoom= this.getOLZoomFromMapObjectZoom(moOldZoom);
+
+                if ( !(newCenter.equals(oldCenter)) || 
+                     !(newZoom == oldZoom) ) {
+
+                    var center = this.getMapObjectLonLatFromOLLonLat(newCenter);
+                    var zoom = this.getMapObjectZoomFromOLZoom(newZoom);
+                    this.setMapObjectCenter(center, zoom);
+                }
+            }
+        }
+    },
+
+
+  /********************************************************/
+  /*                                                      */
+  /*                 Baselayer Functions                  */
+  /*                                                      */
+  /********************************************************/
+
+    /**
+     * @param {OpenLayers.Pixel} viewPortPx
+     *
+     * @returns An OpenLayers.LonLat which is the passed-in view port
+     *          OpenLayers.Pixel, translated into lon/lat by GMAPS
+     *          If gmap is not loaded or not centered, returns null
+     * @type OpenLayers.LonLat
+     */
+    getLonLatFromViewPortPx: function (viewPortPx) {
+        var lonlat = null;
+        if ( (this.mapObject != null) && 
+             (this.getMapObjectCenter() != null) ) {
+            var moPixel = this.getMapObjectPixelFromOLPixel(viewPortPx);
+            var moLonLat = this.getMapObjectLonLatFromMapObjectPixel(moPixel)
+            lonlat = this.getOLLonLatFromMapObjectLonLat(moLonLat);
+        }
+        return lonlat;
+    },
+
+ 
+    /**
+     * @param {OpenLayers.LonLat} lonlat
+     *
+     * @returns An OpenLayers.Pixel which is the passed-in OpenLayers.LonLat, 
+     *          translated into view port pixels BY GMAPS
+     *          If gmap is not loaded or not centered, returns null
+     * @type OpenLayers.Pixel
+     */
+    getViewPortPxFromLonLat: function (lonlat) {
+        var viewPortPx = null;
+        if ( (this.mapObject != null) && 
+             (this.getMapObjectCenter() != null) ) {
+
+            var moLonLat = this.getMapObjectLonLatFromOLLonLat(lonlat);
+            var moPixel = this.getMapObjectPixelFromMapObjectLonLat(moLonLat)
+        
+            viewPortPx = this.getOLPixelFromMapObjectPixel(moPixel);
+        }
+        return viewPortPx;
+    },
+
+  /********************************************************/
+  /*                                                      */
+  /*               Translation Functions                  */
+  /*                                                      */
+  /*   The following functions translate Map Object and   */
+  /*            OL formats for Pixel, LonLat              */
+  /*                                                      */
+  /********************************************************/
+
+  //
+  // TRANSLATION: MapObject LatLng <-> OpenLayers.LonLat
+  //
+
+    /**
+     * @param {Object} moLonLat
+     * 
+     * @returns An OpenLayers.LonLat, translated from the passed in 
+     *          MapObject LonLat
+     *          Returns null if null value is passed in
+     * @type OpenLayers.LonLat
+     */
+    getOLLonLatFromMapObjectLonLat: function(moLonLat) {
+        var olLonLat = null;
+        if (moLonLat != null) {
+            var lon = this.getLongitudeFromMapObjectLonLat(moLonLat);
+            var lat = this.getLatitudeFromMapObjectLonLat(moLonLat);
+            olLonLat = new OpenLayers.LonLat(lon, lat);
+        }
+        return olLonLat;
+    },
+
+    /**
+     * @param {OpenLayers.LonLat} olLonLat
+     * 
+     * @returns A MapObject LonLat, translated from the passed in 
+     *          OpenLayers.LonLat
+     *          Returns null if null value is passed in
+     * @type Object
+     */
+    getMapObjectLonLatFromOLLonLat: function(olLonLat) {
+        var moLatLng = null;
+        if (olLonLat != null) {
+            moLatLng = this.getMapObjectLonLatFromLonLat(olLonLat.lon,
+                                                         olLonLat.lat);
+        }
+        return moLatLng;
+    },
+
+
+  //
+  // TRANSLATION: MapObject Pixel <-> OpenLayers.Pixel
+  //
+
+    /**
+     * @param {Object} moPixel
+     * 
+     * @returns An OpenLayers.Pixel, translated from the passed in 
+     *          MapObject Pixel
+     *          Returns null if null value is passed in
+     * @type OpenLayers.Pixel
+     */
+    getOLPixelFromMapObjectPixel: function(moPixel) {
+        var olPixel = null;
+        if (moPixel != null) {
+            var x = this.getXFromMapObjectPixel(moPixel);
+            var y = this.getYFromMapObjectPixel(moPixel);
+            olPixel = new OpenLayers.Pixel(x, y);
+        }
+        return olPixel;
+    },
+
+    /**
+     * @param {OpenLayers.Pixel} olPixel
+     * 
+     * @returns A MapObject Pixel, translated from the passed in 
+     *          OpenLayers.Pixel
+     *          Returns null if null value is passed in
+     * @type Object
+     */
+    getMapObjectPixelFromOLPixel: function(olPixel) {
+        var moPixel = null;
+        if (olPixel != null) {
+            moPixel = this.getMapObjectPixelFromXY(olPixel.x, olPixel.y);
+        }
+        return moPixel;
+    },
+
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.Layer.EventPane"
+});


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/EventPane.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/FixedZoomLevels.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/FixedZoomLevels.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/FixedZoomLevels.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,234 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
+ * for the full text of the license. */
+
+
+
+/** 
+ *   Some Layers will already have established zoom levels (like google 
+ *    or ve). Instead of trying to determine them and populate a resolutions[]
+ *    Array with those values, we will hijack the resolution functionality
+ *    here.
+ * 
+ *   When you subclass FixedZoomLevels: 
+ * 
+ *   The initResolutions() call gets nullified, meaning no resolutions[] array 
+ *    is set up. Which would be a big problem getResolution() in Layer, since 
+ *    it merely takes map.zoom and indexes into resolutions[]... but....
+ * 
+ *   The getResolution() call is also overridden. Instead of using the 
+ *    resolutions[] array, we simply calculate the current resolution based
+ *    on the current extent and the current map size. But how will we be able
+ *    to calculate the current extent without knowing the resolution...?
+ *  
+ *   The getExtent() function is also overridden. Instead of calculating extent
+ *    based on the center point and the current resolution, we instead 
+ *    calculate the extent by getting the lonlats at the top-left and 
+ *    bottom-right by using the getLonLatFromViewPortPx() translation function,
+ *    taken from the pixel locations (0,0) and the size of the map. But how 
+ *    will we be able to do lonlat-px translation without resolution....?
+ * 
+ *   The getZoomForResolution() method is overridden. Instead of indexing into
+ *    the resolutions[] array, we call OpenLayers.Layer.getExent(), passing in
+ *    the desired resolution. With this extent, we then call getZoomForExtent() 
+ * 
+ * 
+ *   Whenever you implement a layer using OpenLayers.Layer.FixedZoomLevels, 
+ *    it is your responsibility to provide the following three functions:
+ * 
+ *   - getLonLatFromViewPortPx()
+ *   - getViewPortPxFromLonLat()
+ *   - getZoomForExtent()
+ * 
+ *  ...those three functions should generally be provided by any reasonable 
+ *  API that you might be working from.
+ * 
+ * @class
+ */
+OpenLayers.Layer.FixedZoomLevels = OpenLayers.Class.create();
+OpenLayers.Layer.FixedZoomLevels.prototype = {
+      
+  /********************************************************/
+  /*                                                      */
+  /*                 Baselayer Functions                  */
+  /*                                                      */
+  /*    The following functions must all be implemented   */
+  /*                  by all base layers                  */
+  /*                                                      */
+  /********************************************************/
+    
+    /**
+     * @constructor
+     */
+    initialize: function() {
+        //this class is only just to add the following functions... 
+        // nothing to actually do here... but it is probably a good
+        // idea to have layers that use these functions call this 
+        // inititalize() anyways, in case at some point we decide we 
+        // do want to put some functionality or state in here. 
+    },
+    
+    /**
+     * 
+     */
+    initResolutions: function() {
+
+        var props = new Array('minZoomLevel', 'maxZoomLevel', 'numZoomLevels');
+          
+        for(var i=0; i < props.length; i++) {
+            var property = props[i];
+            this[property] = (this.options[property] != null)  
+                                     ? this.options[property] 
+                                     : this.map[property];
+        }
+
+        if ( (this.minZoomLevel == null) ||
+             (this.minZoomLevel < this.MIN_ZOOM_LEVEL) ){
+            this.minZoomLevel = this.MIN_ZOOM_LEVEL;
+        }        
+
+        var limitZoomLevels = this.MAX_ZOOM_LEVEL - this.minZoomLevel + 1;
+        if (this.numZoomLevels != null) {
+            this.numZoomLevels = Math.min(this.numZoomLevels, limitZoomLevels);
+        } else {
+            if (this.maxZoomLevel != null) {
+                var zoomDiff = this.maxZoomLevel - this.minZoomLevel + 1;
+                this.numZoomLevels = Math.min(zoomDiff, limitZoomLevels);
+            } else {
+                this.numZoomLevels = limitZoomLevels;
+            }
+        }
+
+        this.maxZoomLevel = this.minZoomLevel + this.numZoomLevels - 1;
+
+        if (this.RESOLUTIONS != null) {
+            var resolutionsIndex = 0;
+            this.resolutions = [];
+            for(var i= this.minZoomLevel; i < this.numZoomLevels; i++) {
+                this.resolutions[resolutionsIndex++] = this.RESOLUTIONS[i];            
+            }
+        }        
+    },
+    
+    /** 
+     * @returns Degrees per Pixel
+     * @type float
+     */
+    getResolution: function() {
+
+        if (this.resolutions != null) {
+            return OpenLayers.Layer.prototype.getResolution.apply(this, arguments);
+        } else {
+            var resolution = null;
+            
+            var viewSize = this.map.getSize();
+            var extent = this.getExtent();
+            
+            if ((viewSize != null) && (extent != null)) {
+                resolution = Math.max( extent.getWidth()  / viewSize.w,
+                                       extent.getHeight() / viewSize.h );
+            }
+            return resolution;
+        }
+     },
+
+    /** Calculates using px-> lonlat translation functions on tl and br 
+     *   corners of viewport
+     * 
+     * @returns A Bounds object which represents the lon/lat bounds of the 
+     *          current viewPort.
+     * @type OpenLayers.Bounds
+     */
+    getExtent: function () {
+        var extent = null;
+        
+        
+        var size = this.map.getSize();
+        
+        var tlPx = new OpenLayers.Pixel(0,0);
+        var tlLL = this.getLonLatFromViewPortPx(tlPx);
+
+        var brPx = new OpenLayers.Pixel(size.w, size.h);
+        var brLL = this.getLonLatFromViewPortPx(brPx);
+        
+        if ((tlLL != null) && (brLL != null)) {
+            extent = new OpenLayers.Bounds(tlLL.lon, 
+                                       brLL.lat, 
+                                       brLL.lon, 
+                                       tlLL.lat);
+        }
+
+        return extent;
+    },
+
+    /**
+     * @param {float} resolution
+     *
+     * @returns A suitable zoom level for the specified resolution.
+     *          If no baselayer is set, returns null.
+     * @type int
+     */
+    getZoomForResolution: function(resolution) {
+      
+        if (this.resolutions != null) {
+            return OpenLayers.Layer.prototype.getZoomForResolution.apply(this, arguments);
+        } else {
+            var extent = OpenLayers.Layer.prototype.getExtent.apply(this, 
+                                                                    [resolution]);
+                                                                    
+            return this.getZoomForExtent(extent);
+        }
+    },
+
+
+
+    
+  /********************************************************/
+  /*                                                      */
+  /*             Translation Functions                    */
+  /*                                                      */
+  /*    The following functions translate GMaps and OL    */ 
+  /*     formats for Pixel, LonLat, Bounds, and Zoom      */
+  /*                                                      */
+  /********************************************************/
+
+
+  //
+  // TRANSLATION: MapObject Zoom <-> OpenLayers Zoom
+  //
+  
+    /**
+     * @param {int} gZoom
+     * 
+     * @returns An OpenLayers Zoom level, translated from the passed in gZoom
+     *          Returns null if null value is passed in
+     * @type int
+     */
+    getOLZoomFromMapObjectZoom: function(moZoom) {
+        var zoom = null;
+        if (moZoom != null) {
+            zoom = moZoom - this.minZoomLevel;
+        }
+        return zoom;
+    },
+    
+    /**
+     * @param {int} olZoom
+     * 
+     * @returns A MapObject level, translated from the passed in olZoom
+     *          Returns null if null value is passed in
+     * @type int
+     */
+    getMapObjectZoomFromOLZoom: function(olZoom) {
+        var zoom = null; 
+        if (olZoom != null) {
+            zoom = olZoom + this.minZoomLevel;
+        }
+        return zoom;
+    },
+
+
+    /** @final @type String */
+    CLASS_NAME: "FixedZoomLevels.js"
+};
+


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/FixedZoomLevels.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/GeoRSS.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/GeoRSS.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/GeoRSS.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,195 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
+ * for the full text of the license. */
+
+
+/**
+ * @class
+ * 
+ * @requires OpenLayers/Layer/Markers.js
+ */
+OpenLayers.Layer.GeoRSS = OpenLayers.Class.create();
+OpenLayers.Layer.GeoRSS.prototype = 
+  OpenLayers.Class.inherit( OpenLayers.Layer.Markers, {
+
+    /** store url of text file
+    * @type str */
+    location:null,
+
+    /** @type Array(OpenLayers.Feature) */
+    features: null,
+
+    /** @type OpenLayers.Feature */
+    selectedFeature: null,
+
+    /**
+    * @constructor
+    *
+    * @param {String} name
+    * @param {String} location
+    */
+    initialize: function(name, location) {
+        OpenLayers.Layer.Markers.prototype.initialize.apply(this, [name]);
+        this.location = location;
+        this.features = new Array();
+        OpenLayers.loadURL(location, null, this, this.parseData);
+    },
+
+    /**
+     * 
+     */
+    destroy: function() {
+        this.clearFeatures();
+        this.features = null;
+        OpenLayers.Layer.Markers.prototype.destroy.apply(this, arguments);
+    },
+        
+    /**
+     * @param {?} ajaxRequest
+     */
+    parseData: function(ajaxRequest) {
+        var doc = ajaxRequest.responseXML;
+        if (!doc || ajaxRequest.fileType!="XML") {
+            doc = OpenLayers.parseXMLString(ajaxRequest.responseText);
+        }
+        
+        this.name = null;
+        try {
+            this.name = doc.getElementsByTagNameNS('*', 'title')[0].firstChild.nodeValue;
+        }
+        catch (e) {
+            this.name = doc.getElementsByTagName('title')[0].firstChild.nodeValue;
+        }
+       
+        /* Try RSS items first, then Atom entries */
+        var itemlist = null;
+        try {
+            itemlist = doc.getElementsByTagNameNS('*', 'item');
+        }
+        catch (e) {
+            itemlist = doc.getElementsByTagName('item');
+        }
+
+        if (itemlist.length == 0) {
+            try {
+                itemlist = doc.getElementsByTagNameNS('*', 'entry');
+            }
+            catch(e) {
+                itemlist = doc.getElementsByTagName('entry');
+            }
+        }
+
+        for (var i = 0; i < itemlist.length; i++) {
+            var data = {};
+            var point = OpenLayers.Util.getNodes(itemlist[i], 'georss:point');
+            var lat = OpenLayers.Util.getNodes(itemlist[i], 'geo:lat');
+            var lon = OpenLayers.Util.getNodes(itemlist[i], 'geo:long');
+            if (point.length > 0) {
+                var location = point[0].firstChild.nodeValue.split(" ");
+                
+                if (location.length !=2) {
+                    var location = point[0].firstChild.nodeValue.split(",");
+                }
+            } else if (lat.length > 0 && lon.length > 0) {
+                var location = [parseFloat(lat[0].firstChild.nodeValue), parseFloat(lon[0].firstChild.nodeValue)];
+            } else {
+                continue;
+            }
+            location = new OpenLayers.LonLat(parseFloat(location[1]), parseFloat(location[0]));
+            
+            /* Provide defaults for title and description */
+            var title = "Untitled";
+            try {
+              title = OpenLayers.Util.getNodes(itemlist[i], 
+                        "title")[0].firstChild.nodeValue;
+            }
+            catch (e) { title="Untitled"; }
+           
+            /* First try RSS descriptions, then Atom summaries */
+            var descr_nodes = null;
+            try {
+                descr_nodes = itemlist[i].getElementsByTagNameNS("*",
+                                                "description");
+            }
+            catch (e) {
+                descr_nodes = itemlist[i].getElementsByTagName("description");
+            }
+            if (descr_nodes.length == 0) {
+                try {
+                    descr_nodes = itemlist[i].getElementsByTagNameNS("*",
+                                                "summary");
+                }
+                catch (e) {
+                    descr_nodes = itemlist[i].getElementsByTagName("summary");
+                }
+            }
+
+            var description = "No description.";
+            try {
+              description = descr_nodes[0].firstChild.nodeValue;
+            }
+            catch (e) { description="No description."; }
+
+            try { var link = OpenLayers.Util.getNodes(itemlist[i], "link")[0].firstChild.nodeValue; } catch (e) { } 
+            data.icon = OpenLayers.Marker.defaultIcon();
+            data.popupSize = new OpenLayers.Size(250, 120);
+            if ((title != null) && (description != null)) {
+                contentHTML = '<div class="olLayerGeoRSSClose">[x]</div>'; 
+                contentHTML += '<div class="olLayerGeoRSSTitle">';
+                if (link) contentHTML += '<a class="link" href="'+link+'" target="_blank">';
+                contentHTML += title;
+                if (link) contentHTML += '</a>';
+                contentHTML += '</div>';
+                contentHTML += '<div style="" class="olLayerGeoRSSDescription">';
+                contentHTML += description;
+                contentHTML += '</div>';
+                data['popupContentHTML'] = contentHTML;                
+            }
+            var feature = new OpenLayers.Feature(this, location, data);
+            this.features.push(feature);
+            var marker = feature.createMarker();
+            marker.events.register('click', feature, this.markerClick);
+            this.addMarker(marker);
+        }
+    },
+    
+    /**
+     * @param {Event} evt
+     */
+    markerClick: function(evt) {
+        sameMarkerClicked = (this == this.layer.selectedFeature);
+        this.layer.selectedFeature = (!sameMarkerClicked) ? this : null;
+        for(var i=0; i < this.layer.map.popups.length; i++) {
+            this.layer.map.removePopup(this.layer.map.popups[i]);
+        }
+        if (!sameMarkerClicked) {
+            var popup = this.createPopup();
+            OpenLayers.Event.observe(popup.div, "click",
+            function() { 
+              for(var i=0; i < this.layer.map.popups.length; i++) { 
+                this.layer.map.removePopup(this.layer.map.popups[i]); 
+              } 
+            }.bindAsEventListener(this));
+            this.layer.map.addPopup(popup); 
+        }
+        OpenLayers.Event.stop(evt);
+    },
+
+    /**
+     * 
+     */
+    clearFeatures: function() {
+        if (this.features != null) {
+            while(this.features.length > 0) {
+                var feature = this.features[0];
+                OpenLayers.Util.removeItem(this.features, feature);
+                feature.destroy();
+            }
+        }        
+    },
+    
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.Layer.GeoRSS"
+});
+     
+    


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/GeoRSS.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/Google.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/Google.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/Google.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,384 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
+ * for the full text of the license. */
+
+
+/**
+ * @class
+ * 
+ * @requires OpenLayers/Layer/EventPane.js
+ * @requires OpenLayers/Layer/FixedZoomLevels.js
+ */
+OpenLayers.Layer.Google = OpenLayers.Class.create();
+OpenLayers.Layer.Google.prototype =
+  OpenLayers.Class.inherit( OpenLayers.Layer.EventPane, 
+                            OpenLayers.Layer.FixedZoomLevels, {
+    
+    /** @final @type int */
+    MIN_ZOOM_LEVEL: 0,
+    
+    /** @final @type int */
+    MAX_ZOOM_LEVEL: 17,
+
+    /** Hardcode these resolutions so that they are more closely
+     *   tied with the standard wms projection
+     * 
+     * @final @type Array(float) */
+    RESOLUTIONS: [1.40625,0.703125,0.3515625,0.17578125,0.087890625,0.0439453125,0.02197265625,0.010986328125,0.0054931640625,0.00274658203125,0.001373291015625,0.0006866455078125,0.00034332275390625,0.000171661376953125,0.0000858306884765625,0.00004291534423828125],
+
+    /** @type GMapType */
+    type: null,
+
+    /** 
+     * @constructor
+     * 
+     * @param {String} name
+     */
+    initialize: function(name, options) {
+        OpenLayers.Layer.EventPane.prototype.initialize.apply(this, arguments);
+        OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this, 
+                                                                    arguments);
+        this.addContainerPxFunction();
+    },
+    
+    /** Load the GMap and register appropriate event listeners. If we can't 
+     *   load GMap2, then display a warning message.
+     * 
+     * @private
+     */
+    loadMapObject:function() {
+        
+        //has gmaps library has been loaded?
+        try {
+            // create GMap, hide nav controls
+            this.mapObject = new GMap2( this.div );
+
+            // move the ToS and branding stuff up to the pane
+            // thanks a *mil* Erik for thinking of this
+            var poweredBy = this.div.lastChild;
+            this.div.removeChild(poweredBy);
+            this.pane.appendChild(poweredBy);
+            poweredBy.className = "olLayerGooglePoweredBy gmnoprint";
+            poweredBy.style.left = "";
+            poweredBy.style.bottom = "";
+
+            var termsOfUse = this.div.lastChild;
+            this.div.removeChild(termsOfUse);
+            this.pane.appendChild(termsOfUse);
+            termsOfUse.className = "olLayerGoogleCopyright";
+            termsOfUse.style.right = "";
+            termsOfUse.style.bottom = "";
+
+        } catch (e) {
+            // do not crash
+        }
+               
+    },
+
+    /** Overridden from EventPane because if a map type has been specified, 
+     *   we need to attach a listener for the first moveend -- this is how 
+     *   we will know that the map has been centered. Only once the map has 
+     *   been centered is it safe to change the gmap object's map type. 
+     * 
+     * @param {OpenLayers.Map} map
+     */
+    setMap: function(map) {
+        OpenLayers.Layer.EventPane.prototype.setMap.apply(this, arguments);
+
+        if (this.type != null) {
+            this.map.events.register("moveend", this, this.setMapType);
+        }
+    },
+    
+    /** The map has been centered, and a map type was specified, so we 
+     *   set the map type on the gmap object, then unregister the listener
+     *   so that we dont keep doing this every time the map moves.
+     * 
+     * @private
+     */
+    setMapType: function() {
+        if (this.mapObject.getCenter() != null) {
+            this.mapObject.setMapType(this.type);
+            this.map.events.unregister("moveend", this, this.setMapType);
+        }
+    },
+
+    /**
+     * @param {Event} evt
+     */
+    onMapResize: function() {
+        this.mapObject.checkResize();  
+    },
+
+
+    /**
+     * @param {OpenLayers.Bounds} bounds
+     *
+     * @returns Corresponding zoom level for a specified Bounds. 
+     *          If mapObject is not loaded or not centered, returns null
+     * @type int
+     *
+    getZoomForExtent: function (bounds) {
+        var zoom = null;
+        if (this.mapObject != null) {
+            var moBounds = this.getMapObjectBoundsFromOLBounds(bounds);
+            var moZoom = this.getMapObjectZoomFromMapObjectBounds(moBounds);
+
+            //make sure zoom is within bounds    
+            var moZoom = Math.min(Math.max(moZoom, this.minZoomLevel), 
+                                 this.maxZoomLevel);
+
+            zoom = this.getOLZoomFromMapObjectZoom(moZoom);
+        }
+        return zoom;
+    },
+    
+    */
+    
+  //
+  // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds
+  //
+
+    /**
+     * @param {Object} moBounds
+     * 
+     * @returns An OpenLayers.Bounds, translated from the passed-in
+     *          MapObject Bounds
+     *          Returns null if null value is passed in
+     * @type OpenLayers.Bounds
+     */
+    getOLBoundsFromMapObjectBounds: function(moBounds) {
+        var olBounds = null;
+        if (moBounds != null) {
+            var sw = moBounds.getSouthWest();
+            var ne = moBounds.getNorthEast();
+            olBounds = new OpenLayers.Bounds(sw.lng(), 
+                                             sw.lat(), 
+                                             ne.lng(), 
+                                             ne.lat() );
+        }
+        return olBounds;
+    },
+
+    /**
+     * @param {OpenLayers.Bounds} olBounds
+     * 
+     * @returns A MapObject Bounds, translated from olBounds
+     *          Returns null if null value is passed in
+     * @type Object
+     */
+    getMapObjectBoundsFromOLBounds: function(olBounds) {
+        var moBounds = null;
+        if (olBounds != null) {
+            var sw = new GLatLng(olBounds.bottom, olBounds.left);
+            var ne = new GLatLng(olBounds.top, olBounds.right);
+            moBounds = new GLatLngBounds(sw, ne);
+        }
+        return moBounds;
+    },
+    
+
+
+
+
+    /** Hack-on function because GMAPS does not give it to us
+     * 
+     * @param {GLatLng} gLatLng 
+     *
+     * @returns A GPoint specifying gLatLng translated into "Container" coords
+     * @type GPoint
+     */
+    addContainerPxFunction: function() {
+        if (typeof GMap2 != "undefined" && !GMap2.fromLatLngToContainerPixel) {
+          
+            GMap2.prototype.fromLatLngToContainerPixel = function(gLatLng) {
+          
+                // first we translate into "DivPixel"
+                    var gPoint = this.fromLatLngToDivPixel(gLatLng);
+      
+                    // locate the sliding "Div" div
+                //  it seems like "b" is the main div
+                    var div = this.b.firstChild.firstChild;
+      
+                    // adjust by the offset of "Div" and voila!
+                gPoint.x += div.offsetLeft;
+                gPoint.y += div.offsetTop;
+    
+                return gPoint;
+            };
+        }
+    },
+
+    /** 
+     * @return String with information on why layer is broken, how to get
+     *          it working.
+     * @type String
+     */
+    getWarningHTML:function() {
+
+        var html = "";
+        html += "The Google Layer was unable to load correctly.<br>";
+        html += "<br>";
+        html += "To get rid of this message, select a new BaseLayer "
+        html += "in the layer switcher in the upper-right corner.<br>";
+        html += "<br>";
+        html += "Most likely, this is because the Google Maps library";
+        html += " script was either not included, or does not contain the";
+        html += " correct API key for your site.<br>";
+        html += "<br>";
+        html += "Developers: For help getting this working correctly, ";
+        html += "<a href='http://trac.openlayers.org/wiki/Google' "
+        html +=  "target='_blank'>";
+        html +=     "click here";
+        html += "</a>";
+        
+        return html;
+    },
+
+
+    /************************************
+     *                                  *
+     *   MapObject Interface Controls   *
+     *                                  *
+     ************************************/
+
+
+  // Get&Set Center, Zoom
+
+    /** Set the mapObject to the specified center and zoom
+     * 
+     * @param {Object} center MapObject LonLat format
+     * @param {int} zoom MapObject zoom format
+     */
+    setMapObjectCenter: function(center, zoom) {
+        this.mapObject.setCenter(center, zoom); 
+    },
+   
+    /**
+     * @returns the mapObject's current center in Map Object format
+     * @type Object
+     */
+    getMapObjectCenter: function() {
+        return this.mapObject.getCenter();
+    },
+
+    /** 
+     * @returns the mapObject's current zoom, in Map Object format
+     * @type int
+     */
+    getMapObjectZoom: function() {
+        return this.mapObject.getZoom();
+    },
+
+
+  // LonLat - Pixel Translation
+  
+    /** 
+     * @param {Object} moPixel MapObject Pixel format
+     * 
+     * @returns MapObject LonLat translated from MapObject Pixel
+     * @type Object
+     */
+    getMapObjectLonLatFromMapObjectPixel: function(moPixel) {
+        return this.mapObject.fromContainerPixelToLatLng(moPixel);
+    },
+
+    /** 
+     * @param {Object} moPixel MapObject Pixel format
+     * 
+     * @returns MapObject Pixel translated from MapObject LonLat
+     * @type Object
+     */
+    getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {
+        return this.mapObject.fromLatLngToContainerPixel(moLonLat);
+    },
+
+  
+  // Bounds
+  
+    /** 
+     * @param {Object} moBounds MapObject Bounds format
+     * 
+     * @returns MapObject Zoom for specified MapObject Bounds
+     * @type Object
+     */
+    getMapObjectZoomFromMapObjectBounds: function(moBounds) {
+        return this.mapObject.getBoundsZoomLevel(moBounds);
+    },
+
+    /************************************
+     *                                  *
+     *       MapObject Primitives       *
+     *                                  *
+     ************************************/
+
+
+  // LonLat
+    
+    /**
+     * @param {Object} moLonLat MapObject LonLat format
+     * 
+     * @returns Longitude of the given MapObject LonLat
+     * @type float
+     */
+    getLongitudeFromMapObjectLonLat: function(moLonLat) {
+        return moLonLat.lng();  
+    },
+
+    /**
+     * @param {Object} moLonLat MapObject LonLat format
+     * 
+     * @returns Latitude of the given MapObject LonLat
+     * @type float
+     */
+    getLatitudeFromMapObjectLonLat: function(moLonLat) {
+        return moLonLat.lat();  
+    },
+    
+    /**
+     * @param {int} lon float
+     * @param {int} lat float
+     * 
+     * @returns MapObject LonLat built from lon and lat params
+     * @type Object
+     */
+    getMapObjectLonLatFromLonLat: function(lon, lat) {
+        return new GLatLng(lat, lon);
+    },
+
+  // Pixel
+    
+    /** 
+     * @param {Object} moPixel MapObject Pixel format
+     * 
+     * @returns X value of the MapObject Pixel
+     * @type int
+     */
+    getXFromMapObjectPixel: function(moPixel) {
+        return moPixel.x;
+    },
+
+    /** 
+     * @param {Object} moPixel MapObject Pixel format
+     * 
+     * @returns Y value of the MapObject Pixel
+     * @type int
+     */
+    getYFromMapObjectPixel: function(moPixel) {
+        return moPixel.y;
+    },
+
+    /** 
+     * @param {int} x
+     * @param {int} y
+     * 
+     * @returns MapObject Pixel from x and y parameters
+     * @type Object
+     */
+    getMapObjectPixelFromXY: function(x, y) {
+        return new GPoint(x, y);
+    },
+
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.Layer.Google"
+});


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/Google.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/Grid.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/Grid.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/Grid.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,401 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
+ * for the full text of the license. */
+
+
+/**
+ * @class
+ * 
+ * @requires OpenLayers/Layer/HTTPRequest.js
+ */
+OpenLayers.Layer.Grid = OpenLayers.Class.create();
+OpenLayers.Layer.Grid.prototype = 
+  OpenLayers.Class.inherit( OpenLayers.Layer.HTTPRequest, {
+    
+    /** @type OpenLayers.Size */
+    tileSize: null,
+    
+    /** this is an array of rows, each row is an array of tiles
+     * 
+     * @type Array(Array) */
+    grid: null,
+
+    /** @type Integer */
+    buffer: 2,
+
+    /**
+     * @constructor
+     * 
+     * @param {String} name
+     * @param {String} url
+     * @param {Object} params
+     * @param {Object} options Hashtable of extra options to tag onto the layer
+    */
+    initialize: function(name, url, params, options) {
+        OpenLayers.Layer.HTTPRequest.prototype.initialize.apply(this, 
+                                                                arguments);
+        this.grid = new Array();
+    },
+
+    /** on destroy, clear the grid.
+     *
+     */
+    destroy: function() {
+        this.clearGrid();
+        this.grid = null;
+        this.tileSize = null;
+        OpenLayers.Layer.HTTPRequest.prototype.destroy.apply(this, arguments); 
+    },
+
+    /**
+     * @param {Object} obj
+     * 
+     * @returns An exact clone of this OpenLayers.Layer.Grid
+     * @type OpenLayers.Layer.Grid
+     */
+    clone: function (obj) {
+        
+        if (obj == null) {
+            obj = new OpenLayers.Layer.Grid(this.name,
+                                            this.url,
+                                            this.params,
+                                            this.options);
+        }
+
+        //get all additions from superclasses
+        obj = OpenLayers.Layer.HTTPRequest.prototype.clone.apply(this, [obj]);
+
+        // copy/set any non-init, non-simple values here
+        if (this.tileSize != null) {
+            obj.tileSize = this.tileSize.clone();
+        }
+        
+        // we do not want to copy reference to grid, so we make a new array
+        obj.grid = new Array();
+
+        return obj;
+    },    
+
+    /** When the layer is added to a map, then we can ask the map for
+     *   its default tile size
+     * 
+     * @param {OpenLayers.Map} map
+     */
+    setMap: function(map) {
+        OpenLayers.Layer.HTTPRequest.prototype.setMap.apply(this, arguments);
+        if (this.tileSize == null) {
+            this.tileSize = this.map.getTileSize();
+        }
+    },
+
+    /** This function is called whenever the map is moved. All the moving
+     * of actual 'tiles' is done by the map, but moveTo's role is to accept
+     * a bounds and make sure the data that that bounds requires is pre-loaded.
+     * 
+     * @param {OpenLayers.Bounds} bounds
+     * @param {Boolean} zoomChanged
+     * @param {Boolean} dragging
+     */
+    moveTo:function(bounds, zoomChanged, dragging) {
+        OpenLayers.Layer.HTTPRequest.prototype.moveTo.apply(this, arguments);
+        
+        if (bounds == null) {
+            bounds = this.map.getExtent();
+        }
+        if (bounds != null) {
+            if (!this.grid.length || zoomChanged 
+                || !this.getGridBounds().containsBounds(bounds, true)) { 
+                this._initTiles();
+            } else {
+                while (true) {
+                    var tlLayer = this.grid[0][0].position;
+                    var tlViewPort = 
+                        this.map.getViewPortPxFromLayerPx(tlLayer);
+                    if (tlViewPort.x > -this.tileSize.w * (this.buffer - 1)) {
+                        this.shiftColumn(true);
+                    } else if (tlViewPort.x < -this.tileSize.w * this.buffer) {
+                        this.shiftColumn(false);
+                    } else if (tlViewPort.y > -this.tileSize.h * (this.buffer - 1)) {
+                        this.shiftRow(true);
+                    } else if (tlViewPort.y < -this.tileSize.h * this.buffer) {
+                        this.shiftRow(false);
+                    } else {
+                        break;
+                    }
+                }
+            }
+        }
+    },
+    
+    /**
+     * @private
+     * 
+     * @returns A Bounds object representing the bounds of all the currently 
+     *           loaded tiles (including those partially or not at all seen 
+     *           onscreen)
+     * @type OpenLayers.Bounds
+     */
+    getGridBounds:function() {
+        
+        var bottom = this.grid.length - 1;
+        var bottomLeftTile = this.grid[bottom][0];
+
+        var right = this.grid[0].length - 1; 
+        var topRightTile = this.grid[0][right];
+
+        return new OpenLayers.Bounds(bottomLeftTile.bounds.left, 
+                                     bottomLeftTile.bounds.bottom,
+                                     topRightTile.bounds.right, 
+                                     topRightTile.bounds.top);
+    },
+
+    /**
+     * @private
+     */
+    _initTiles:function() {
+        var viewSize = this.map.getSize();
+        var bounds = this.map.getExtent();
+        var extent = this.map.getMaxExtent();
+        var resolution = this.map.getResolution();
+        var tilelon = resolution * this.tileSize.w;
+        var tilelat = resolution * this.tileSize.h;
+        
+        var offsetlon = bounds.left - extent.left;
+        var tilecol = Math.floor(offsetlon/tilelon) - this.buffer;
+        var tilecolremain = offsetlon/tilelon - tilecol;
+        var tileoffsetx = -tilecolremain * this.tileSize.w;
+        var tileoffsetlon = extent.left + tilecol * tilelon;
+        
+        var offsetlat = bounds.top - (extent.bottom + tilelat);  
+        var tilerow = Math.ceil(offsetlat/tilelat) + this.buffer;
+        var tilerowremain = tilerow - offsetlat/tilelat;
+        var tileoffsety = -tilerowremain * this.tileSize.h;
+        var tileoffsetlat = extent.bottom + tilerow * tilelat;
+        
+        tileoffsetx = Math.round(tileoffsetx); // heaven help us
+        tileoffsety = Math.round(tileoffsety);
+
+        this.origin = new OpenLayers.Pixel(tileoffsetx, tileoffsety);
+
+        var startX = tileoffsetx; 
+        var startLon = tileoffsetlon;
+
+        var rowidx = 0;
+    
+        do {
+            var row = this.grid[rowidx++];
+            if (!row) {
+                row = new Array();
+                this.grid.push(row);
+            }
+
+            tileoffsetlon = startLon;
+            tileoffsetx = startX;
+            var colidx = 0;
+ 
+            do {
+                var tileBounds = new OpenLayers.Bounds(tileoffsetlon, 
+                                                      tileoffsetlat, 
+                                                      tileoffsetlon + tilelon,
+                                                      tileoffsetlat + tilelat);
+
+                var x = tileoffsetx;
+                x -= parseInt(this.map.layerContainerDiv.style.left);
+
+                var y = tileoffsety;
+                y -= parseInt(this.map.layerContainerDiv.style.top);
+
+                var px = new OpenLayers.Pixel(x, y);
+                var tile = row[colidx++];
+                if (!tile) {
+                    tile = this.addTile(tileBounds, px);
+                    row.push(tile);
+                } else {
+                    tile.moveTo(tileBounds, px, false);
+                }
+     
+                tileoffsetlon += tilelon;       
+                tileoffsetx += this.tileSize.w;
+            } while (tileoffsetlon <= bounds.right + tilelon * this.buffer)  
+            
+            tileoffsetlat -= tilelat;
+            tileoffsety += this.tileSize.h;
+        } while(tileoffsetlat >= bounds.bottom - tilelat * this.buffer)
+
+        //now actually draw the tiles
+        this.spiralTileLoad();
+    },
+    
+    /** 
+     * @private 
+     * 
+     *   Starts at the top right corner of the grid and proceeds in a spiral 
+     *    towards the center, adding tiles one at a time to the beginning of a 
+     *    queue. 
+     * 
+     *   Once all the grid's tiles have been added to the queue, we go back 
+     *    and iterate through the queue (thus reversing the spiral order from 
+     *    outside-in to inside-out), calling draw() on each tile. 
+     */
+    spiralTileLoad: function() {
+        var tileQueue = new Array();
+ 
+        var directions = ["right", "down", "left", "up"];
+
+        var iRow = 0;
+        var iCell = -1;
+        var direction = OpenLayers.Util.indexOf(directions, "right");
+        var directionsTried = 0;
+        
+        while( directionsTried < directions.length) {
+
+            var testRow = iRow;
+            var testCell = iCell;
+
+            switch (directions[direction]) {
+                case "right":
+                    testCell++;
+                    break;
+                case "down":
+                    testRow++;
+                    break;
+                case "left":
+                    testCell--;
+                    break;
+                case "up":
+                    testRow--;
+                    break;
+            } 
+    
+            // if the test grid coordinates are within the bounds of the 
+            //  grid, get a reference to the tile.
+            var tile = null;
+            if ((testRow < this.grid.length) && (testRow >= 0) &&
+                (testCell < this.grid[0].length) && (testCell >= 0)) {
+                tile = this.grid[testRow][testCell];
+            }
+            
+            if ((tile != null) && (!tile.queued)) {
+                //add tile to beginning of queue, mark it as queued.
+                tileQueue.unshift(tile);
+                tile.queued = true;
+                
+                //restart the directions counter and take on the new coords
+                directionsTried = 0;
+                iRow = testRow;
+                iCell = testCell;
+            } else {
+                //need to try to load a tile in a different direction
+                direction = (direction + 1) % 4;
+                directionsTried++;
+            }
+        } 
+        
+        // now we go through and draw the tiles in forward order
+        for(var i=0; i < tileQueue.length; i++) {
+            var tile = tileQueue[i]
+            tile.draw();
+            //mark tile as unqueued for the next time (since tiles are reused)
+            tile.queued = false;       
+        }
+    },
+
+    /**
+     * addTile gives subclasses of Grid the opportunity to create an 
+     * OpenLayer.Tile of their choosing. The implementer should initialize 
+     * the new tile and take whatever steps necessary to display it.
+     *
+     * @param {OpenLayers.Bounds} bounds
+     *
+     * @returns The added OpenLayers.Tile
+     * @type OpenLayers.Tile
+     */
+    addTile:function(bounds, position) {
+        // Should be implemented by subclasses
+    },
+
+    
+    /** go through and remove all tiles from the grid, calling
+     *    destroy() on each of them to kill circular references
+     * 
+     * @private
+     */
+    clearGrid:function() {
+        if (this.grid) {
+            for(var iRow=0; iRow < this.grid.length; iRow++) {
+                var row = this.grid[iRow];
+                for(var iCol=0; iCol < row.length; iCol++) {
+                    OpenLayers.Util.clearArray(row[iCol]);
+                }
+            }
+        }
+    },
+
+    /**
+     * @private 
+     * 
+     * @param {Boolean} prepend if true, prepend to beginning.
+     *                          if false, then append to end
+     */
+    shiftRow:function(prepend) {
+        var modelRowIndex = (prepend) ? 0 : (this.grid.length - 1);
+        var modelRow = this.grid[modelRowIndex];
+
+        var resolution = this.map.getResolution();
+        var deltaY = (prepend) ? -this.tileSize.h : this.tileSize.h;
+        var deltaLat = resolution * -deltaY;
+
+        var row = (prepend) ? this.grid.pop() : this.grid.shift();
+
+        for (var i=0; i < modelRow.length; i++) {
+            var modelTile = modelRow[i];
+            var bounds = modelTile.bounds.clone();
+            var position = modelTile.position.clone();
+            bounds.bottom = bounds.bottom + deltaLat;
+            bounds.top = bounds.top + deltaLat;
+            position.y = position.y + deltaY;
+            row[i].moveTo(bounds, position);
+        }
+
+        if (prepend) {
+            this.grid.unshift(row);
+        } else {
+            this.grid.push(row);
+        }
+    },
+
+    /**
+     * @private
+     * 
+     * @param {Boolean} prepend if true, prepend to beginning.
+     *                          if false, then append to end
+     */
+    shiftColumn: function(prepend) {
+        var deltaX = (prepend) ? -this.tileSize.w : this.tileSize.w;
+        var resolution = this.map.getResolution();
+        var deltaLon = resolution * deltaX;
+
+        for (var i=0; i<this.grid.length; i++) {
+            var row = this.grid[i];
+            var modelTileIndex = (prepend) ? 0 : (row.length - 1);
+            var modelTile = row[modelTileIndex];
+            
+            var bounds = modelTile.bounds.clone();
+            var position = modelTile.position.clone();
+            bounds.left = bounds.left + deltaLon;
+            bounds.right = bounds.right + deltaLon;
+            position.x = position.x + deltaX;
+
+            var tile = prepend ? this.grid[i].pop() : this.grid[i].shift()
+            tile.moveTo(bounds, position);
+            if (prepend) {
+                this.grid[i].unshift(tile);
+            } else {
+                this.grid[i].push(tile);
+            }
+        }
+    },
+    
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.Layer.Grid"
+});


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/Grid.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/HTTPRequest.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/HTTPRequest.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/HTTPRequest.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,140 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
+ * for the full text of the license. */
+
+
+/**
+ * @class
+ * 
+ * @requires OpenLayers/Layer.js
+ */
+OpenLayers.Layer.HTTPRequest = OpenLayers.Class.create();
+OpenLayers.Layer.HTTPRequest.prototype = 
+  OpenLayers.Class.inherit( OpenLayers.Layer, {
+
+    /** @type String */
+    url: null,
+
+    /** Hashtable of key/value parameters
+     * @type Object */
+    params: null,
+    
+    /** Whether layer should reproject itself based on base layer locations.
+     *  This allows reprojection onto commercial layers. Default is false:
+     *  Most layers can't reproject, but layers which can create non-square
+     *  geographic pixels can, like WMS. 
+     *
+     * @type Boolean 
+     */
+    reproject: false,
+
+    /**
+     * @constructor
+     * 
+     * @param {String} name
+     * @param {String} url
+     * @param {Object} params
+     * @param {Object} options Hashtable of extra options to tag onto the layer
+     */
+    initialize: function(name, url, params, options) {
+        var newArguments = arguments;
+        newArguments = [name, options];
+        OpenLayers.Layer.prototype.initialize.apply(this, newArguments);
+        this.url = url;
+        this.params = OpenLayers.Util.extend( new Object(), params);
+    },
+
+    /**
+     * 
+     */
+    destroy: function() {
+        this.url = null;
+        this.params = null;
+        OpenLayers.Layer.prototype.destroy.apply(this, arguments); 
+    },
+    
+    /**
+     * @param {Object} obj
+     * 
+     * @returns An exact clone of this OpenLayers.Layer.HTTPRequest
+     * @type OpenLayers.Layer.HTTPRequest
+     */
+    clone: function (obj) {
+        
+        if (obj == null) {
+            obj = new OpenLayers.Layer.HTTPRequest(this.name,
+                                                   this.url,
+                                                   this.params,
+                                                   this.options);
+        }
+        
+        //get all additions from superclasses
+        obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]);
+
+        // copy/set any non-init, non-simple values here
+        
+        return obj;
+    },
+
+    /** 
+     * @param {String} newUrl
+     */
+    setUrl: function(newUrl) {
+        this.url = newUrl;
+    },
+
+    /**
+     * @param {Object} newParams
+     */
+    mergeNewParams:function(newParams) {
+        this.params = OpenLayers.Util.extend(this.params, newParams);
+    },
+    
+
+    /** combine url with layer's params and these newParams. 
+     *   
+     *    does checking on the serverPath variable, allowing for cases when it 
+     *     is supplied with trailing ? or &, as well as cases where not. 
+     *
+     *    return in formatted string like this:
+     *        "server?key1=value1&key2=value2&key3=value3"
+     * 
+     * @param {Object} newParams
+     * @param {String} altUrl Use this as the url instead of the layer's url
+     * 
+     * @type String
+     */
+    getFullRequestString:function(newParams, altUrl) {
+        
+        // use layer's url unless altUrl passed in
+        var url = (altUrl == null) ? this.url : altUrl;
+        
+        // requestString always starts with url
+        var requestString = url;        
+
+        // create a new params hashtable with all the layer params and the 
+        // new params together. then convert to string
+        var allParams = OpenLayers.Util.extend(new Object(), this.params);
+        var allParams = OpenLayers.Util.extend(allParams, newParams);
+        var paramsString = OpenLayers.Util.getParameterString(allParams);
+
+        if (paramsString != "") {
+            var lastServerChar = this.url.charAt(this.url.length - 1);
+            if ((lastServerChar == "&") || (lastServerChar == "?")) {
+                requestString += paramsString;
+            } else {
+                if (this.url.indexOf('?') == -1) {
+                    //serverPath has no ? -- add one
+                    requestString += '?' + paramsString;
+                } else {
+                    //serverPath contains ?, so must already have paramsString at the end
+                    requestString += '&' + paramsString;
+                }
+            }
+        }
+        return requestString;
+    },
+
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.Layer.HTTPRequest"
+});


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/HTTPRequest.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/Image.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/Image.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/Image.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,161 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
+ * for the full text of the license. */
+ 
+/**
+ * @fileoverview Image Layer
+ * @author Tim Schaub
+ */
+
+/**
+ * @class
+ * 
+ * @requires OpenLayers/Layer.js
+ */
+OpenLayers.Layer.Image = OpenLayers.Class.create();
+OpenLayers.Layer.Image.prototype = 
+  OpenLayers.Class.inherit(OpenLayers.Layer, {
+
+    /** By default, Layer.Image will be a baselayer
+     * 
+     * @type Boolean */
+    isBaseLayer: true,
+    
+    /** @type String */
+    url: null,
+
+    /** @type OpenLayers.Bounds */
+    extent: null,
+    
+    /** @type OpenLayers.Size */
+    size: null,
+
+    /** @type OpenLayers.Tile.Image */
+    tile: null,
+
+    /** The ratio of height/width represented by a single pixel in the graphic
+     * 
+     * @type Float */
+    aspectRatio: null,
+
+    /**
+    * @constructor
+    *
+    * @param {String} name
+    * @param {String} url Relative or absolute path to the image
+    * @param {OpenLayers.Bounds} extent The extent represented by the image
+    * @param {OpenLayers.Size} size The size (in pixels) of the image
+    * @param {Object} options Hashtable of extra options to tag onto the layer
+    */
+    initialize: function(name, url, extent, size, options) {
+        this.url = url;
+        this.extent = extent;
+        this.size = size;
+        OpenLayers.Layer.prototype.initialize.apply(this, [name, options]);
+
+        this.aspectRatio = (this.extent.getHeight() / this.size.h) /
+                           (this.extent.getWidth() / this.size.w);
+    },    
+
+    /**
+     * 
+     */
+    destroy: function() {
+        this.tile.destroy();
+        this.tile = null;
+        OpenLayers.Layer.prototype.destroy.apply(this, arguments);
+    },
+    
+    /**
+     * @param {Object} obj
+     * 
+     * @returns An exact clone of this OpenLayers.Layer.Image
+     * @type OpenLayers.Layer.Image
+     */
+    clone: function(obj) {
+        
+        if(obj == null) {
+            obj = new OpenLayers.Layer.Image(this.name,
+                                               this.url,
+                                               this.extent,
+                                               this.size,
+                                               this.options);
+        }
+
+        //get all additions from superclasses
+        obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]);
+
+        // copy/set any non-init, non-simple values here
+
+        return obj;
+    },    
+    
+    /**
+     * @param {OpenLayers.Map} map
+     */
+    setMap: function(map) {
+        // If nothing to do with resolutions has been set, assume a single
+        //  resolution determined by extent/size
+        if( this.options.maxResolution == null ) {
+        	this.options.maxResolution = this.extent.getWidth() / this.size.w;
+        }
+        OpenLayers.Layer.prototype.setMap.apply(this, arguments);
+    },
+
+    /** Create the tile for the image or resize it for the new resolution
+     * 
+     * @param {OpenLayers.Bounds} bounds
+     * @param {Boolean} zoomChanged
+     * @param {Boolean} dragging
+     */
+    moveTo:function(bounds, zoomChanged, dragging) {
+        OpenLayers.Layer.prototype.moveTo.apply(this, arguments);
+
+        var firstRendering = (this.tile == null);
+
+        if(zoomChanged || firstRendering) {
+
+            //determine new tile size
+            var tileWidth = this.extent.getWidth() / this.map.getResolution();
+            var tileHeight = this.extent.getHeight() /
+                             (this.map.getResolution() * this.aspectRatio);
+            var tileSize = new OpenLayers.Size(tileWidth, tileHeight);
+            
+            //determine new position (upper left corner of new bounds)
+            var ul = new OpenLayers.LonLat(this.extent.left, this.extent.top);
+            var ulPx = this.map.getLayerPxFromLonLat(ul);
+
+            if(firstRendering) {
+                //create the new tile
+                this.tile = new OpenLayers.Tile.Image(this, ulPx, this.extent, 
+                                                      this.url, tileSize);
+            } else {
+                //just resize the tile and set it's new position
+                this.tile.size = tileSize.clone();
+                this.tile.position = ulPx.clone();
+            }
+            this.tile.draw();
+        }
+    }, 
+    
+    /**
+     * @param {String} newUrl
+     */
+    setUrl: function(newUrl) {
+        this.url = newUrl;
+        this.draw();
+    },
+
+    /** The url we return is always the same (the image itself never changes)
+     *   so we can ignore the bounds parameter (it will always be the same, 
+     *   anyways) 
+     * 
+     * @param {OpenLayers.Bounds} bounds
+     */
+    getURL: function(bounds) {
+        return this.url;
+    },
+        
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.Layer.Image"
+});


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/Image.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/KaMap.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/KaMap.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/KaMap.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,152 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
+ * for the full text of the license. */
+
+
+/**
+ * @class
+ * 
+ * @requires OpenLayers/Layer/Grid.js
+ */
+OpenLayers.Layer.KaMap = OpenLayers.Class.create();
+OpenLayers.Layer.KaMap.prototype = 
+  OpenLayers.Class.inherit( OpenLayers.Layer.Grid, {
+
+    /** KaMap Layer is always a base layer 
+     * 
+     * @type Boolean
+     */    
+    isBaseLayer: true,
+    
+    units: null,
+
+    resolution: OpenLayers.DOTS_PER_INCH,
+    
+    DEFAULT_PARAMS: {
+        i: 'jpeg',
+        map: ''
+    },
+        
+    initialize: function(name, url, params, options) {
+        var newArguments = new Array();
+        newArguments.push(name, url, params, options);
+        OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);
+        this.params = (params ? params : {});
+        if (params) {
+            OpenLayers.Util.applyDefaults(
+                           this.params, 
+                           this.DEFAULT_PARAMS
+                           );
+        }
+    },
+
+    /**
+     * @param {OpenLayers.Bounds} bounds
+     * 
+     * @returns A string with the layer's url and parameters and also the 
+     *           passed-in bounds and appropriate tile size specified as 
+     *           parameters
+     * @type String
+     */
+    getURL: function (bounds) {
+        var mapRes = this.map.getResolution();
+        var scale = Math.round((this.map.getScale() * 10000)) / 10000;        
+        var cellSize = new OpenLayers.Size(mapRes*this.tileSize.w, mapRes*this.tileSize.h);
+        var pX = Math.round(((bounds.left) / cellSize.w) * this.tileSize.w);
+        var pY = -Math.round(((bounds.top) / cellSize.h) * this.tileSize.h);
+        return this.getFullRequestString(
+                      { t: pY, 
+                        l: pX,
+                        s: scale
+                      });
+    },
+    
+    addTile:function(bounds,position) {
+        var url = this.getURL(bounds);
+        return new OpenLayers.Tile.Image(this, position, bounds, 
+                                             url, this.tileSize);
+    },
+    
+    _initTiles:function() {
+
+        var viewSize = this.map.getSize();
+        var bounds = this.map.getExtent();
+        var extent = this.map.getMaxExtent();
+        var resolution = this.map.getResolution();
+        var tilelon = resolution*this.tileSize.w;
+        var tilelat = resolution*this.tileSize.h;
+        
+        var offsetlon = bounds.left;
+        var tilecol = Math.floor(offsetlon/tilelon);
+        var tilecolremain = offsetlon/tilelon - tilecol;
+        var tileoffsetx = -tilecolremain * this.tileSize.w;
+        var tileoffsetlon = tilecol * tilelon;
+        
+        var offsetlat = bounds.top;  
+        var tilerow = Math.ceil(offsetlat/tilelat);
+        var tilerowremain = tilerow - offsetlat/tilelat;
+        var tileoffsety = -(tilerowremain+1) * this.tileSize.h;
+        var tileoffsetlat = tilerow * tilelat;
+        
+        tileoffsetx = Math.round(tileoffsetx); // heaven help us
+        tileoffsety = Math.round(tileoffsety);
+
+        this.origin = new OpenLayers.Pixel(tileoffsetx,tileoffsety);
+
+        var startX = tileoffsetx; 
+        var startLon = tileoffsetlon;
+        
+        var rowidx = 0;
+        
+        do {
+            var row;
+            
+            row = this.grid[rowidx++];
+            if (!row) {
+                row = new Array();
+                this.grid.push(row);
+            }
+
+            tileoffsetlon = startLon;
+            tileoffsetx = startX;
+
+            var colidx = 0;
+ 
+            do {
+                var tileBounds = new OpenLayers.Bounds(tileoffsetlon, 
+                                                      tileoffsetlat, 
+                                                      tileoffsetlon + tilelon,
+                                                      tileoffsetlat + tilelat);
+
+                var x = tileoffsetx;
+                x -= parseInt(this.map.layerContainerDiv.style.left);
+
+                var y = tileoffsety;
+                y -= parseInt(this.map.layerContainerDiv.style.top);
+
+                var px = new OpenLayers.Pixel(x, y);
+                var tile;
+                
+                tile = row[colidx++];
+                if (!tile) {
+                    tile = this.addTile(tileBounds, px);
+                    row.push(tile);
+                } else {
+                    tile.moveTo(tileBounds, px, false);
+                }
+     
+                tileoffsetlon += tilelon;       
+                tileoffsetx += this.tileSize.w;
+            } while (tileoffsetlon <= bounds.right + tilelon * this.buffer)  
+            
+            tileoffsetlat -= tilelat;
+            tileoffsety += this.tileSize.h;
+        } while(tileoffsetlat >= bounds.bottom - tilelat * this.buffer)
+        
+        this.spiralTileLoad();
+
+    },
+
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.Layer.KaMap"
+});


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/KaMap.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/MapServer.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/MapServer.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/MapServer.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,101 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
+ * for the full text of the license. */
+// @requires OpenLayers/Layer/Grid.js
+/**
+* @class
+*/
+OpenLayers.Layer.MapServer = OpenLayers.Class.create();
+OpenLayers.Layer.MapServer.prototype =
+  OpenLayers.Class.inherit( OpenLayers.Layer.Grid, {
+
+    /** @final @type hash */
+    DEFAULT_PARAMS: {
+                      mode: "map",
+                      map_imagetype: "png"
+                     },
+
+    /**
+    * @constructor
+    *
+    * @param {str} name
+    * @param {str} url
+    * @param {hash} params
+    */
+    initialize: function(name, url, params) {
+        var newArguments = new Array();
+        //uppercase params
+        params = OpenLayers.Util.upperCaseObject(params);
+        newArguments.push(name, url, params);
+        OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);
+
+        if (arguments.length > 0) {
+            OpenLayers.Util.applyDefaults(
+                           this.params,
+                           OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS)
+                           );
+        }
+    },
+
+
+    /** 
+     * @type Boolean
+     */
+    isBaseLayer: function() {
+        return (this.params.TRANSPARENT != 'true');
+    },
+
+    /**
+    * @param {String} name
+    * @param {hash} params
+    *
+    * @returns A clone of this OpenLayers.Layer.MapServer, with the passed-in
+    *          parameters merged in.
+    * @type OpenLayers.Layer.MapServer
+    */
+    clone: function (name, params) {
+        var mergedParams = {};
+        OpenLayers.Util.extend(mergedParams, this.params);
+        OpenLayers.Util.extend(mergedParams, params);
+        var obj = new OpenLayers.Layer.MapServer(name, this.url, mergedParams);
+        obj.setTileSize(this.tileSize);
+        return obj;
+    },
+
+    /**
+    * addTile creates a tile, initializes it (via 'draw' in this case), and
+    * adds it to the layer div.
+    *
+    * @param {OpenLayers.Bounds} bounds
+    *
+    * @returns The added OpenLayers.Tile.Image
+    * @type OpenLayers.Tile.Image
+    */
+    addTile:function(bounds,position) {
+        var url = this.getURL(bounds);
+        return new OpenLayers.Tile.Image(this, position, bounds, url, this.tileSize);
+    },
+        
+    /**
+     * @param {OpenLayers.Bounds} bounds
+     * 
+     * @returns A string with the layer's url and parameters and also the 
+     *           passed-in bounds and appropriate tile size specified as 
+     *           parameters
+     * @type String
+     */
+    getURL: function (bounds) {
+
+        var url = this.getFullRequestString(
+                     {mapext:bounds.toBBOX().replace(/,/g,"+"),
+                      imgext:bounds.toBBOX().replace(/,/g,"+"),
+                      map_size:this.tileSize.w+'+'+this.tileSize.h,
+                      imgx: this.tileSize.w/2,
+                      imgy: this.tileSize.h/2,
+                      imgxy: this.tileSize.w+"+"+this.tileSize.h
+                      });
+        return url;
+    },
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.Layer.MapServer"
+});


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/MapServer.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/Markers.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/Markers.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/Markers.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,123 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
+ * for the full text of the license. */
+
+
+/**
+ * @class
+ * 
+ * @requires OpenLayers/Layer.js
+ */
+OpenLayers.Layer.Markers = OpenLayers.Class.create();
+OpenLayers.Layer.Markers.prototype = 
+  OpenLayers.Class.inherit( OpenLayers.Layer, {
+    
+    /** Markers layer is never a base layer. 
+     * 
+     * @type Boolean
+     */
+    isBaseLayer: false,
+    
+    /** internal marker list
+    * @type Array(OpenLayers.Marker) */
+    markers: null,
+    
+    /**
+    * @constructor
+    *
+    * @param {String} name
+    * @param {Object} options Hashtable of extra options to tag onto the layer
+    */
+    initialize: function(name, options) {
+        OpenLayers.Layer.prototype.initialize.apply(this, arguments);
+        this.markers = new Array();
+    },
+    
+    /**
+     * 
+     */
+    destroy: function() {
+        this.clearMarkers();
+        markers = null;
+        OpenLayers.Layer.prototype.destroy.apply(this, arguments);
+    },
+
+    
+    /** 
+     * @param {OpenLayers.Bounds} bounds
+     * @param {Boolean} zoomChanged
+     * @param {Boolean} dragging
+     */
+    moveTo:function(bounds, zoomChanged, dragging) {
+        OpenLayers.Layer.prototype.moveTo.apply(this, arguments);
+
+        if (zoomChanged) {
+            this.redraw();
+        }
+    },
+
+    /**
+     * @param {OpenLayers.Marker} marker
+     */
+    addMarker: function(marker) {
+        this.markers.push(marker);
+        if (this.map && this.map.getExtent()) {
+            marker.map = this.map;
+            this.drawMarker(marker);
+        }
+    },
+
+    /**
+     * @param {OpenLayers.Marker} marker
+     */
+    removeMarker: function(marker) {
+        OpenLayers.Util.removeItem(this.markers, marker);
+        if ((marker.icon != null) && (marker.icon.imageDiv != null) &&
+            (marker.icon.imageDiv.parentNode == this.div) ) {
+            this.div.removeChild(marker.icon.imageDiv);    
+        }
+    },
+
+    /**
+     * 
+     */
+    clearMarkers: function() {
+        if (this.markers != null) {
+            while(this.markers.length > 0) {
+                this.removeMarker(this.markers[0]);
+            }
+        }
+    },
+
+    /** clear all the marker div's from the layer and then redraw all of them.
+    *    Use the map to recalculate new placement of markers.
+    */
+    redraw: function() {
+        for(i=0; i < this.markers.length; i++) {
+            this.drawMarker(this.markers[i]);
+        }
+    },
+
+    /** Calculate the pixel location for the marker, create it, and 
+    *    add it to the layer's div
+    * 
+    * @private 
+    * 
+    * @param {OpenLayers.Marker} marker
+    */
+    drawMarker: function(marker) {
+        var px = this.map.getLayerPxFromLonLat(marker.lonlat);
+        if (px == null) {
+            marker.display(false);
+        } else {
+            var markerImg = marker.draw(px);
+            if (!marker.drawn) {
+                this.div.appendChild(markerImg);
+                marker.drawn = true;
+            }
+        }
+    },
+    
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.Layer.Markers"
+});


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/Markers.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/MultiMap.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/MultiMap.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/MultiMap.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,214 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
+ * for the full text of the license. */
+
+/**
+ * @class
+ * 
+ * @requires OpenLayers/Layer/EventPane.js
+ * @requires OpenLayers/Layer/FixedZoomLevels.js
+ */
+OpenLayers.Layer.MultiMap = OpenLayers.Class.create();
+OpenLayers.Layer.MultiMap.prototype =
+  OpenLayers.Class.inherit( OpenLayers.Layer.EventPane, 
+                            OpenLayers.Layer.FixedZoomLevels, {
+    
+    /** @final @type int */
+    MIN_ZOOM_LEVEL: 1,
+    
+    /** @final @type int */
+    MAX_ZOOM_LEVEL: 17,
+
+    /** Hardcode these resolutions so that they are more closely
+     *   tied with the standard wms projection
+     * 
+     * @final @type Array(float) */
+    RESOLUTIONS: [9, 1.40625,0.703125,0.3515625,0.17578125,0.087890625,0.0439453125,0.02197265625,0.010986328125,0.0054931640625,0.00274658203125,0.001373291015625,0.0006866455078125,0.00034332275390625,0.000171661376953125,0.0000858306884765625,0.00004291534423828125],
+
+    /** @type VEMapType */
+    type: null,
+
+    /** 
+     * @constructor
+     * 
+     * @param {String} name
+     */
+    initialize: function(name, options) {
+        OpenLayers.Layer.EventPane.prototype.initialize.apply(this, arguments);
+        OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this, 
+                                                                    arguments);
+    },
+    
+    /**
+     * 
+     */
+    loadMapObject:function() {
+        try { //crash proofing
+            this.mapObject = new MultimapViewer(this.div);
+        } catch (e) { }
+    },
+
+    /** 
+     * @return String with information on why layer is broken, how to get
+     *          it working.
+     * @type String
+     */
+    getWarningHTML:function() {
+
+        var html = "";
+        html += "The MM Layer was unable to load correctly.<br>";
+        html += "<br>";
+        html += "To get rid of this message, select a new BaseLayer "
+        html += "in the layer switcher in the upper-right corner.<br>";
+        html += "<br>";
+        html += "Most likely, this is because the MM library";
+        html += " script was either not correctly included.<br>";
+        html += "<br>";
+        html += "Demmlopers: For help getting this working correctly, ";
+        html += "<a href='http://trac.openlayers.org/wiki/MultiMap' "
+        html +=  "target='_blank'>";
+        html +=     "click here";
+        html += "</a>";
+
+        return html;
+    },
+
+
+
+    /************************************
+     *                                  *
+     *   MapObject Interface Controls   *
+     *                                  *
+     ************************************/
+
+
+  // Get&Set Center, Zoom
+
+    /** Set the mapObject to the specified center and zoom
+     * 
+     * @param {Object} center MapObject LonLat format
+     * @param {int} zoom MapObject zoom format
+     */
+    setMapObjectCenter: function(center, zoom) {
+        this.mapObject.goToPosition(center, zoom); 
+    },
+   
+    /**
+     * @returns the mapObject's current center in Map Object format
+     * @type Object
+     */
+    getMapObjectCenter: function() {
+        return this.mapObject.getCurrentPosition();
+    },
+
+    /** 
+     * @returns the mapObject's current zoom, in Map Object format
+     * @type int
+     */
+    getMapObjectZoom: function() {
+        return this.mapObject.getZoomFactor();
+    },
+
+
+  // LonLat - Pixel Translation
+  
+    /** 
+     * @param {Object} moPixel MapObject Pixel format
+     * 
+     * @returns MapObject LonLat translated from MapObject Pixel
+     * @type Object
+     */
+    getMapObjectLonLatFromMapObjectPixel: function(moPixel) {
+        moPixel.x = moPixel.x - (this.map.getSize().w/2);
+        moPixel.y = moPixel.y - (this.map.getSize().h/2);
+        return this.mapObject.getMapPositionAt(moPixel);
+    },
+
+    /** 
+     * @param {Object} moPixel MapObject Pixel format
+     * 
+     * @returns MapObject Pixel translated from MapObject LonLat
+     * @type Object
+     */
+    getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {
+        return this.mapObject.geoPosToContainerPixels(moLonLat);
+    },
+
+
+    /************************************
+     *                                  *
+     *       MapObject Primitives       *
+     *                                  *
+     ************************************/
+
+
+  // LonLat
+    
+    /**
+     * @param {Object} moLonLat MapObject LonLat format
+     * 
+     * @returns Longitude of the given MapObject LonLat
+     * @type float
+     */
+    getLongitudeFromMapObjectLonLat: function(moLonLat) {
+        return moLonLat.lon;
+    },
+
+    /**
+     * @param {Object} moLonLat MapObject LonLat format
+     * 
+     * @returns Latitude of the given MapObject LonLat
+     * @type float
+     */
+    getLatitudeFromMapObjectLonLat: function(moLonLat) {
+        return moLonLat.lat;
+    },
+
+    /**
+     * @param {int} lon float
+     * @param {int} lat float
+     * 
+     * @returns MapObject LonLat built from lon and lat params
+     * @type Object
+     */
+    getMapObjectLonLatFromLonLat: function(lon, lat) {
+        return new MMLatLon(lat, lon);
+    },
+
+  // Pixel
+    
+    /** 
+     * @param {Object} moPixel MapObject Pixel format
+     * 
+     * @returns X value of the MapObject Pixel
+     * @type int
+     */
+    getXFromMapObjectPixel: function(moPixel) {
+        return moPixel.x;
+    },
+
+    /** 
+     * @param {Object} moPixel MapObject Pixel format
+     * 
+     * @returns Y value of the MapObject Pixel
+     * @type int
+     */
+    getYFromMapObjectPixel: function(moPixel) {
+        return moPixel.y;
+    },
+
+    /** 
+     * @param {int} x
+     * @param {int} y
+     * 
+     * @returns MapObject Pixel from x and y parameters
+     * @type Object
+     */
+    getMapObjectPixelFromXY: function(x, y) {
+        return new MMPoint(x, y);
+    },
+
+
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.Layer.MultiMap"
+});


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/MultiMap.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/Text.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/Text.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/Text.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,169 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
+ * for the full text of the license. */
+
+
+/**
+ * @class
+ * 
+ * @requires OpenLayers/Layer/Markers.js
+ */
+OpenLayers.Layer.Text = OpenLayers.Class.create();
+OpenLayers.Layer.Text.prototype = 
+  OpenLayers.Class.inherit( OpenLayers.Layer.Markers, {
+
+    /** store url of text file - this should be specified in the 
+     *   "options" hashtable
+    * @type str */
+    location:null,
+
+    /** @type Array(OpenLayers.Feature) */
+    features: null,
+
+    /** @type OpenLayers.Feature */
+    selectedFeature: null,
+
+    /**
+    * @constructor
+    *
+    * @param {String} name
+    * @param {String} location
+    * @param {Object} options Hashtable of extra options to tag onto the layer
+    */
+    initialize: function(name, options) {
+        OpenLayers.Layer.Markers.prototype.initialize.apply(this, arguments);
+        this.features = new Array();
+        if (this.location != null) {
+            OpenLayers.loadURL(this.location, null, this, this.parseData);
+        }
+    },
+
+   /**
+     * 
+     */
+    destroy: function() {
+        this.clearFeatures();
+        this.features = null;
+        OpenLayers.Layer.Markers.prototype.destroy.apply(this, arguments);
+    },
+    
+    
+    /**
+     * @param {?} ajaxRequest
+     */
+    parseData: function(ajaxRequest) {
+        var text = ajaxRequest.responseText;
+        var lines = text.split('\n');
+        var columns;
+        // length - 1 to allow for trailing new line
+        for (var lcv = 0; lcv < (lines.length - 1); lcv++) {
+            var currLine = lines[lcv].replace(/^\s*/,'').replace(/\s*$/,'');
+        
+            if (currLine.charAt(0) != '#') { /* not a comment */
+            
+                if (!columns) {
+                    //First line is columns
+                    columns = currLine.split('\t');
+                } else {
+                    var vals = currLine.split('\t');
+                    var location = new OpenLayers.LonLat(0,0);
+                    var title; var url;
+                    var icon, iconSize, iconOffset;
+                    var set = false;
+                    for (var valIndex = 0; valIndex < vals.length; valIndex++) {
+                        if (vals[valIndex]) {
+                            if (columns[valIndex] == 'point') {
+                                var coords = vals[valIndex].split(',');
+                                location.lat = parseFloat(coords[0]);
+                                location.lon = parseFloat(coords[1]);
+                                set = true;
+                            } else if (columns[valIndex] == 'lat') {
+                                location.lat = parseFloat(vals[valIndex]);
+                                set = true;
+                            } else if (columns[valIndex] == 'lon') {
+                                location.lon = parseFloat(vals[valIndex]);
+                                set = true;
+                            } else if (columns[valIndex] == 'title')
+                                title = vals[valIndex];
+                            else if (columns[valIndex] == 'image' ||
+                                     columns[valIndex] == 'icon')
+                                url = vals[valIndex];
+                            else if (columns[valIndex] == 'iconSize') {
+                                var size = vals[valIndex].split(',');
+                                iconSize = new OpenLayers.Size(parseFloat(size[0]),
+                                                           parseFloat(size[1]));
+                            } else if (columns[valIndex] == 'iconOffset') {
+                                var offset = vals[valIndex].split(',');
+                                iconOffset = new OpenLayers.Pixel(parseFloat(offset[0]),
+                                                           parseFloat(offset[1]));
+                            } else if (columns[valIndex] == 'title') {
+                                title = vals[valIndex];
+                            } else if (columns[valIndex] == 'description') {
+                                description = vals[valIndex];
+                            }
+                        }
+                    }
+                    if (set) {
+                      var data = new Object();
+                      if (url != null) {
+                          data.icon = new OpenLayers.Icon(url, 
+                                                          iconSize, 
+                                                          iconOffset);
+                      } else {
+                          data.icon = OpenLayers.Marker.defaultIcon();
+
+                          //allows for the case where the image url is not 
+                          // specified but the size is. use a default icon
+                          // but change the size
+                          if (iconSize != null) {
+                              data.icon.setSize(iconSize);
+                          }
+                        
+                      }
+                      if ((title != null) && (description != null)) {
+                          data['popupContentHTML'] = '<h2>'+title+'</h2><p>'+description+'</p>';
+                      }
+                      var feature = new OpenLayers.Feature(this, location, data);
+                      this.features.push(feature);
+                      var marker = feature.createMarker();
+                      marker.events.register('click', feature, this.markerClick);
+                      this.addMarker(marker);
+                    }
+                }
+            }
+        }
+    },
+    
+    /**
+     * @param {Event} evt
+     */
+    markerClick: function(evt) {
+        sameMarkerClicked = (this == this.layer.selectedFeature);
+        this.layer.selectedFeature = (!sameMarkerClicked) ? this : null;
+        for(var i=0; i < this.layer.map.popups.length; i++) {
+            this.layer.map.removePopup(this.layer.map.popups[i]);
+        }
+        if (!sameMarkerClicked) {
+            this.layer.map.addPopup(this.createPopup()); 
+        }
+        OpenLayers.Event.stop(evt);
+    },
+
+    /**
+     * 
+     */
+    clearFeatures: function() {
+        if (this.features != null) {
+            while(this.features.length > 0) {
+                var feature = this.features[0];
+                OpenLayers.Util.removeItem(this.features, feature);
+                feature.destroy();
+            }
+        }        
+    },
+    
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.Layer.Text"
+});
+     
+    


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/Text.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/VirtualEarth.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/VirtualEarth.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/VirtualEarth.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,228 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
+ * for the full text of the license. */
+
+
+/**
+ * @class
+ * 
+ * @requires OpenLayers/Layer/EventPane.js
+ * @requires OpenLayers/Layer/FixedZoomLevels.js
+ */
+OpenLayers.Layer.VirtualEarth = OpenLayers.Class.create();
+OpenLayers.Layer.VirtualEarth.prototype =
+  OpenLayers.Class.inherit( OpenLayers.Layer.EventPane, 
+                            OpenLayers.Layer.FixedZoomLevels, {
+    
+    /** @final @type int */
+    MIN_ZOOM_LEVEL: 1,
+    
+    /** @final @type int */
+    MAX_ZOOM_LEVEL: 17,
+
+    /** Hardcode these resolutions so that they are more closely
+     *   tied with the standard wms projection
+     * 
+     * @final @type Array(float) */
+    RESOLUTIONS: [1.40625,0.703125,0.3515625,0.17578125,0.087890625,0.0439453125,0.02197265625,0.010986328125,0.0054931640625,0.00274658203125,0.001373291015625,0.0006866455078125,0.00034332275390625,0.000171661376953125,0.0000858306884765625,0.00004291534423828125],
+
+    /** @type VEMapType */
+    type: null,
+
+    /** 
+     * @constructor
+     * 
+     * @param {String} name
+     */
+    initialize: function(name, options) {
+        OpenLayers.Layer.EventPane.prototype.initialize.apply(this, arguments);
+        OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this, 
+                                                                    arguments);
+    },
+    
+    /**
+     * 
+     */
+    loadMapObject:function() {
+
+        // create div and set to same size as map
+        var veDiv = OpenLayers.Util.createDiv(this.name);
+        var sz = this.map.getSize();
+        veDiv.style.width = sz.w;
+        veDiv.style.height = sz.h;
+        this.div.appendChild(veDiv);
+
+        try { // crash prevention
+            this.mapObject = new VEMap(this.name);
+        } catch (e) { }
+
+        if (this.mapObject != null) {
+            try { // this is to catch a Mozilla bug without falling apart
+                this.mapObject.LoadMap(null, null, this.type);
+            } catch (e) { }
+            this.mapObject.HideDashboard();
+        }
+    },
+
+    /** 
+     * @return String with information on why layer is broken, how to get
+     *          it working.
+     * @type String
+     */
+    getWarningHTML:function() {
+
+        var html = "";
+        html += "The VE Layer was unable to load correctly.<br>";
+        html += "<br>";
+        html += "To get rid of this message, select a new BaseLayer "
+        html += "in the layer switcher in the upper-right corner.<br>";
+        html += "<br>";
+        html += "Most likely, this is because the VE library";
+        html += " script was either not correctly included.<br>";
+        html += "<br>";
+        html += "Developers: For help getting this working correctly, ";
+        html += "<a href='http://trac.openlayers.org/wiki/VirtualEarth' "
+        html +=  "target='_blank'>";
+        html +=     "click here";
+        html += "</a>";
+
+        return html;
+    },
+
+
+
+    /************************************
+     *                                  *
+     *   MapObject Interface Controls   *
+     *                                  *
+     ************************************/
+
+
+  // Get&Set Center, Zoom
+
+    /** Set the mapObject to the specified center and zoom
+     * 
+     * @param {Object} center MapObject LonLat format
+     * @param {int} zoom MapObject zoom format
+     */
+    setMapObjectCenter: function(center, zoom) {
+        this.mapObject.SetCenterAndZoom(center, zoom); 
+    },
+   
+    /**
+     * @returns the mapObject's current center in Map Object format
+     * @type Object
+     */
+    getMapObjectCenter: function() {
+        return this.mapObject.GetCenter();
+    },
+
+    /** 
+     * @returns the mapObject's current zoom, in Map Object format
+     * @type int
+     */
+    getMapObjectZoom: function() {
+        return this.mapObject.GetZoomLevel();
+    },
+
+
+  // LonLat - Pixel Translation
+  
+    /** 
+     * @param {Object} moPixel MapObject Pixel format
+     * 
+     * @returns MapObject LonLat translated from MapObject Pixel
+     * @type Object
+     */
+    getMapObjectLonLatFromMapObjectPixel: function(moPixel) {
+        return this.mapObject.PixelToLatLong(moPixel.x, moPixel.y);
+    },
+
+    /** 
+     * @param {Object} moPixel MapObject Pixel format
+     * 
+     * @returns MapObject Pixel translated from MapObject LonLat
+     * @type Object
+     */
+    getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {
+        return this.mapObject.LatLongToPixel(moLonLat);
+    },
+
+
+    /************************************
+     *                                  *
+     *       MapObject Primitives       *
+     *                                  *
+     ************************************/
+
+
+  // LonLat
+    
+    /**
+     * @param {Object} moLonLat MapObject LonLat format
+     * 
+     * @returns Longitude of the given MapObject LonLat
+     * @type float
+     */
+    getLongitudeFromMapObjectLonLat: function(moLonLat) {
+        return moLonLat.Longitude;
+    },
+
+    /**
+     * @param {Object} moLonLat MapObject LonLat format
+     * 
+     * @returns Latitude of the given MapObject LonLat
+     * @type float
+     */
+    getLatitudeFromMapObjectLonLat: function(moLonLat) {
+        return moLonLat.Latitude;
+    },
+
+    /**
+     * @param {int} lon float
+     * @param {int} lat float
+     * 
+     * @returns MapObject LonLat built from lon and lat params
+     * @type Object
+     */
+    getMapObjectLonLatFromLonLat: function(lon, lat) {
+        return new VELatLong(lat, lon);
+    },
+
+  // Pixel
+    
+    /** 
+     * @param {Object} moPixel MapObject Pixel format
+     * 
+     * @returns X value of the MapObject Pixel
+     * @type int
+     */
+    getXFromMapObjectPixel: function(moPixel) {
+        return moPixel.x;
+    },
+
+    /** 
+     * @param {Object} moPixel MapObject Pixel format
+     * 
+     * @returns Y value of the MapObject Pixel
+     * @type int
+     */
+    getYFromMapObjectPixel: function(moPixel) {
+        return moPixel.y;
+    },
+
+    /** 
+     * @param {int} x
+     * @param {int} y
+     * 
+     * @returns MapObject Pixel from x and y parameters
+     * @type Object
+     */
+    getMapObjectPixelFromXY: function(x, y) {
+        return new Msn.VE.Pixel(x, y);
+    },
+
+
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.Layer.VirtualEarth"
+});


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/VirtualEarth.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/WFS.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/WFS.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/WFS.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,225 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
+ * for the full text of the license. */
+
+
+/**
+ * @class
+ * 
+ * @requires OpenLayers/Layer/Grid.js
+ * @requires OpenLayers/Layer/Markers.js
+ */
+OpenLayers.Layer.WFS = OpenLayers.Class.create();
+OpenLayers.Layer.WFS.prototype = 
+  OpenLayers.Class.inherit( OpenLayers.Layer.Grid,
+                            OpenLayers.Layer.Markers, {
+
+    /** WFS layer is never a base layer. 
+     * 
+     * @type Boolean
+     */
+    isBaseLayer: false,
+
+    buffer: 1,
+    
+    /** Allow the user to specify special classes for features and tiles.
+     * 
+     *   This allows for easy-definition of behaviour. The defaults are 
+     *    set here, but to override it, the property should be set via 
+     *    the "options" parameter.
+     */
+     
+    /** @type Object */
+    featureClass: OpenLayers.Feature.WFS,
+
+    /** @type Object */
+    tileClass: OpenLayers.Tile.WFS,
+
+    /** Hashtable of default key/value parameters
+     * @final @type Object */
+    DEFAULT_PARAMS: { service: "WFS",
+                      version: "1.0.0",
+                      request: "GetFeature",
+                      typename: "docpoint"
+                    },
+
+    /**
+    * @constructor
+    *
+    * @param {String} name
+    * @param {String} url
+    * @param {Object} params
+    * @param {Object} options Hashtable of extra options to tag onto the layer
+    */
+    initialize: function(name, url, params, options) {
+        var newArguments = new Array();
+        //uppercase params
+        params = OpenLayers.Util.upperCaseObject(params);
+        newArguments.push(name, url, params, options);
+        OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);
+
+        var newArguments = new Array();
+        //uppercase params
+        newArguments.push(name, options);
+        OpenLayers.Layer.Markers.prototype.initialize.apply(this, newArguments);
+    
+        OpenLayers.Util.applyDefaults(
+                       this.params, 
+                       OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS)
+                       );
+    },    
+    
+
+    /**
+     * 
+     */
+    destroy: function() {
+        OpenLayers.Layer.Grid.prototype.destroy.apply(this, arguments);
+        OpenLayers.Layer.Markers.prototype.destroy.apply(this, arguments);
+    },
+    
+    /**
+     * @param {OpenLayers.Map} map
+     */
+    setMap: function(map) {
+        OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments);
+        OpenLayers.Layer.Markers.prototype.setMap.apply(this, arguments);
+    },
+    
+    /** 
+     * @param {OpenLayers.Bounds} bounds
+     * @param {Boolean} zoomChanged
+     * @param {Boolean} dragging
+     */
+    moveTo:function(bounds, zoomChanged, dragging) {
+        OpenLayers.Layer.Grid.prototype.moveTo.apply(this, arguments);
+        OpenLayers.Layer.Markers.prototype.moveTo.apply(this, arguments);
+    },
+        
+    /**
+     * @param {Object} obj
+     * 
+     * @returns An exact clone of this OpenLayers.Layer.WMS
+     * @type OpenLayers.Layer.WMS
+     */
+    clone: function (obj) {
+        
+        if (obj == null) {
+            obj = new OpenLayers.Layer.WFS(this.name,
+                                           this.url,
+                                           this.params,
+                                           this.options);
+        }
+
+        //get all additions from superclasses
+        obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);
+
+        // copy/set any non-init, non-simple values here
+
+        return obj;
+    },    
+
+
+    /**
+    * addTile creates a tile, initializes it (via 'draw' in this case), and 
+    * adds it to the layer div. 
+    *
+    * @param {OpenLayers.Bounds} bounds
+    *
+    * @returns The added OpenLayers.Tile.WFS
+    * @type OpenLayers.Tile.WFS
+    */
+    addTile:function(bounds, position) {
+        var urls = new Array();
+
+        //add standard URL
+        urls.push( this.getFullRequestString() );
+
+        if (this.urls != null) {
+            // if there are more urls, add them.
+            for(var i=0; i < this.urls.length; i++) {
+                urls.push( this.getFullRequestString(null, this.urls[i]) );
+            }
+        }
+
+        return new this.tileClass(this, position, bounds, 
+                                           urls, this.tileSize);
+    },
+
+
+
+    /**
+     * Catch changeParams and uppercase the new params to be merged in
+     *  before calling changeParams on the super class.
+     * 
+     * Once params have been changed, we will need to re-init our tiles
+     * 
+     * @param {Object} newParams Hashtable of new params to use
+     */
+    mergeNewParams:function(newParams) {
+        var upperParams = OpenLayers.Util.upperCaseObject(newParams);
+        var newArguments = [upperParams];
+        OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(this, newArguments);
+
+        if (this.grid != null) {
+            this._initTiles();
+        }
+    },
+
+    /** combine the layer's url with its params and these newParams. 
+    *   
+    *    Add the SRS parameter from 'projection' -- this is probably
+    *     more eloquently done via a setProjection() method, but this 
+    *     works for now and always.
+    * 
+    * @param {Object} newParams
+    * 
+    * @type String
+    */
+    getFullRequestString:function(newParams) {
+        var projection = this.map.getProjection();
+        this.params.SRS = (projection == "none") ? null : projection;
+
+        return OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(
+                                                    this, arguments);
+    },
+
+
+    /** 
+     * @param {String} featureID
+     * 
+     * @returns The Feature, found within one of the layer's tiles' features 
+     *          array, with a matching id.
+     *          If none found or if null passed-in, returns null
+     * @type OpenLayers.Feature
+     */
+    getFeature: function(featureID) {
+        var foundFeature = null;
+        if (featureID != null) {
+
+            if (this.grid) {
+    
+                for(var iRow = 0; iRow < this.grid.length; iRow++) {
+                    var row = this.grid[iRow];
+                    for(var iCol = 0; iCol < row.length; iCol++) {
+                        var tile = row[iCol];
+    
+                        for(var i=0; i < tile.features.length; i++) {
+                            var feature = tile.features[i];
+                            if (feature.id == featureID) {
+                                foundFeature = feature;
+                            }
+                        }
+                    }
+                }
+            }
+
+        }
+        return foundFeature; 
+    },
+
+
+
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.Layer.WFS"
+});


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/WFS.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/WMS/Untiled.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/WMS/Untiled.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/WMS/Untiled.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,219 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
+ * for the full text of the license. */
+
+ 
+/**
+ * @class
+ * 
+ * @requires OpenLayers/Layer/HTTPRequest.js
+ * @requires OpenLayers/Layer/WMS.js
+ */
+OpenLayers.Layer.WMS.Untiled = OpenLayers.Class.create();
+OpenLayers.Layer.WMS.Untiled.prototype = 
+  OpenLayers.Class.inherit( OpenLayers.Layer.HTTPRequest, {
+
+    /** Hashtable of default parameter key/value pairs
+     * @final @type Object */
+    DEFAULT_PARAMS: { service: "WMS",
+                      version: "1.1.1",
+                      request: "GetMap",
+                      styles: "",
+                      exceptions: "application/vnd.ogc.se_inimage",
+                      format: "image/jpeg"
+                     },
+    reproject: true,
+
+    /** the ratio of image/tile size to map size (this is the untiled buffer)
+     * @type int */
+    ratio: 2,
+
+    /** @type OpenLayers.Tile.Image */
+    tile: null,
+    
+
+    /**
+    * @constructor
+    *
+    * @param {String} name
+    * @param {String} url
+    * @param {Object} params
+    */
+    initialize: function(name, url, params, options) {
+        var newArguments = new Array();
+        //uppercase params
+        params = OpenLayers.Util.upperCaseObject(params);
+        newArguments.push(name, url, params, options);
+        OpenLayers.Layer.HTTPRequest.prototype.initialize.apply(this, 
+                                                                newArguments);
+        OpenLayers.Util.applyDefaults(
+                       this.params, 
+                       OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS)
+                       );
+
+        // unless explicitly set in options, if the layer is transparent, 
+        // it will be an overlay        
+        if ((options == null) || (options.isBaseLayer == null)) {
+            this.isBaseLayer = ((this.params.TRANSPARENT != "true") && 
+                                (this.params.TRANSPARENT != true));
+        }
+    },    
+
+    /**
+     * 
+     */
+    destroy: function() {
+        this.tile.destroy();
+        this.tile = null;    
+        OpenLayers.Layer.HTTPRequest.prototype.destroy.apply(this, arguments);
+    },
+    
+    /**
+     * @param {Object} obj
+     * 
+     * @returns An exact clone of this OpenLayers.Layer.WMS.Untiled
+     * @type OpenLayers.Layer.WMS.Untiled
+     */
+    clone: function (obj) {
+        
+        if (obj == null) {
+            obj = new OpenLayers.Layer.WMS.Untiled(this.name,
+                                                   this.url,
+                                                   this.params,
+                                                   this.options);
+        }
+
+        //get all additions from superclasses
+        obj = OpenLayers.Layer.HTTPRequest.prototype.clone.apply(this, [obj]);
+
+        // copy/set any non-init, non-simple values here
+
+        return obj;
+    },    
+    
+    
+    /** Once HTTPRequest has set the map, we can load the image div
+     * 
+     * @param {OpenLayers.Map} map
+     */
+    setMap: function(map) {
+        OpenLayers.Layer.HTTPRequest.prototype.setMap.apply(this, arguments);
+    },
+
+    /** When it is not a dragging move (ie when done dragging)
+     *   reload and recenter the div.
+     * 
+     * @param {OpenLayers.Bounds} bounds
+     * @param {Boolean} zoomChanged
+     * @param {Boolean} dragging
+     */
+    moveTo:function(bounds, zoomChanged, dragging) {
+        OpenLayers.Layer.HTTPRequest.prototype.moveTo.apply(this,arguments);
+        
+        if (bounds == null) {
+            bounds = this.map.getExtent();
+        }
+
+        var firstRendering = (this.tile == null);
+
+        //does the new bounds to which we need to move fall outside of the 
+        // current tile's bounds?
+        var outOfBounds = (!firstRendering &&
+                           !this.tile.bounds.containsBounds(bounds));
+
+        if ( zoomChanged || firstRendering || (!dragging && outOfBounds) ) {
+
+            //clear out the old tile 
+            if (this.tile) {
+                this.tile.clear();
+            }
+
+            //determine new tile bounds
+            var center = bounds.getCenterLonLat();
+            var tileWidth = bounds.getWidth() * this.ratio;
+            var tileHeight = bounds.getHeight() * this.ratio;
+            var tileBounds = 
+                new OpenLayers.Bounds(center.lon - (tileWidth / 2),
+                                      center.lat - (tileHeight / 2),
+                                      center.lon + (tileWidth / 2),
+                                      center.lat + (tileHeight / 2));
+
+            //determine new tile size
+            var tileSize = this.map.getSize();
+            tileSize.w = tileSize.w * this.ratio;
+            tileSize.h = tileSize.h * this.ratio;
+
+            //formulate request url string
+            var url = this.getURL(tileBounds); 
+
+            //determine new position (upper left corner of new bounds)
+            var ul = new OpenLayers.LonLat(tileBounds.left, tileBounds.top);
+            var pos = this.map.getLayerPxFromLonLat(ul);
+
+            if ( this.tile && !this.tile.size.equals(tileSize)) {
+                this.tile.destroy();
+                this.tile = null;
+            }
+
+            if (!this.tile) {
+                this.tile = new OpenLayers.Tile.Image(this, pos, tileBounds, 
+                                                     url, tileSize);
+                this.tile.draw();
+            } else {
+                this.tile.moveTo(tileBounds, pos);
+            } 
+    
+        }
+    },
+    
+    getURL: function(bounds) {
+        var tileSize = this.map.getSize();
+        tileSize.w = tileSize.w * this.ratio;
+        tileSize.h = tileSize.h * this.ratio;
+        return this.getFullRequestString( {'BBOX': bounds.toBBOX(),
+                                                  'WIDTH': tileSize.w,
+                                                  'HEIGHT': tileSize.h} );
+    },
+ 
+    
+    /** Once HTTPRequest has updated the url, reload the image div
+     * @param {String} newUrl
+     */
+    setUrl: function(newUrl) {
+        OpenLayers.Layer.HTTPRequest.prototype.setUrl.apply(this, arguments);
+        this.moveTo();
+    },
+
+    /** Once HTTPRequest has updated new params, reload the image div
+     * @param {Object} newParams
+     */
+    mergeNewParams:function(newParams) {
+        var upperParams = OpenLayers.Util.upperCaseObject(newParams);
+        var newArguments = [upperParams];
+        OpenLayers.Layer.HTTPRequest.prototype.mergeNewParams.apply(this, 
+                                                                 newArguments);
+        //redraw
+        this.moveTo();
+    },
+    
+    /** combine the layer's url with its params and these newParams. 
+    *   
+    *    Add the SRS parameter from 'projection' -- this is probably
+    *     more eloquently done via a setProjection() method, but this 
+    *     works for now and always.
+    * 
+    * @param {Object} newParams
+    * 
+    * @type String
+    */
+    getFullRequestString:function(newParams) {
+        var projection = this.map.getProjection();
+        this.params.SRS = (projection == "none") ? null : projection;
+
+        return OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(
+                                                    this, arguments);
+    },
+    
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.Layer.WMS.Untiled"
+});


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/WMS/Untiled.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/WMS.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/WMS.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/WMS.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,155 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
+ * for the full text of the license. */
+
+
+/**
+ * @class
+ * 
+ * @requires OpenLayers/Layer/Grid.js
+ */
+OpenLayers.Layer.WMS = OpenLayers.Class.create();
+OpenLayers.Layer.WMS.prototype = 
+  OpenLayers.Class.inherit( OpenLayers.Layer.Grid, {
+
+    /** Hashtable of default parameter key/value pairs 
+     * @final @type Object */
+    DEFAULT_PARAMS: { service: "WMS",
+                      version: "1.1.1",
+                      request: "GetMap",
+                      styles: "",
+                      exceptions: "application/vnd.ogc.se_inimage",
+                      format: "image/jpeg"
+                     },
+    
+    reproject: true,
+
+    /**
+    * @constructor
+    *
+    * @param {String} name
+    * @param {String} url
+    * @param {Object} params
+    * @param {Object} options Hashtable of extra options to tag onto the layer
+    */
+    initialize: function(name, url, params, options) {
+        var newArguments = new Array();
+        //uppercase params
+        params = OpenLayers.Util.upperCaseObject(params);
+        newArguments.push(name, url, params, options);
+        OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);
+        OpenLayers.Util.applyDefaults(
+                       this.params, 
+                       OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS)
+                       );
+
+        // unless explicitly set in options, if the layer is transparent, 
+        // it will be an overlay
+        if (options == null || options.isBaseLayer == null) {
+            this.isBaseLayer = ((this.params.TRANSPARENT != "true") && 
+                                (this.params.TRANSPARENT != true));
+        }
+    },    
+
+    /**
+     * 
+     */
+    destroy: function() {
+        // for now, nothing special to do here. 
+        OpenLayers.Layer.Grid.prototype.destroy.apply(this, arguments);  
+    },
+
+    
+    /**
+     * @param {Object} obj
+     * 
+     * @returns An exact clone of this OpenLayers.Layer.WMS
+     * @type OpenLayers.Layer.WMS
+     */
+    clone: function (obj) {
+        
+        if (obj == null) {
+            obj = new OpenLayers.Layer.WMS(this.name,
+                                           this.url,
+                                           this.params,
+                                           this.options);
+        }
+
+        //get all additions from superclasses
+        obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);
+
+        // copy/set any non-init, non-simple values here
+
+        return obj;
+    },    
+    
+    /**
+     * @param {OpenLayers.Bounds} bounds
+     * 
+     * @returns A string with the layer's url and parameters and also the 
+     *           passed-in bounds and appropriate tile size specified as 
+     *           parameters
+     * @type String
+     */
+    getURL: function (bounds) {
+        return this.getFullRequestString(
+                     {BBOX:bounds.toBBOX(),
+                      WIDTH:this.tileSize.w,
+                      HEIGHT:this.tileSize.h});
+    },
+
+    /**
+    * addTile creates a tile, initializes it, and 
+    * adds it to the layer div. 
+    *
+    * @param {OpenLayers.Bounds} bounds
+    *
+    * @returns The added OpenLayers.Tile.Image
+    * @type OpenLayers.Tile.Image
+    */
+    addTile:function(bounds,position) {
+        url = this.getURL(bounds);
+        return new OpenLayers.Tile.Image(this, position, bounds, 
+                                             url, this.tileSize);
+    },
+
+    /**
+     * Catch changeParams and uppercase the new params to be merged in
+     *  before calling changeParams on the super class.
+     * 
+     * Once params have been changed, we will need to re-init our tiles
+     * 
+     * @param {Object} newParams Hashtable of new params to use
+     */
+    mergeNewParams:function(newParams) {
+        var upperParams = OpenLayers.Util.upperCaseObject(newParams);
+        var newArguments = [upperParams];
+        OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(this, 
+                                                             newArguments);
+
+        if (this.map != null) {
+            this._initTiles();
+        }
+    },
+
+    /** combine the layer's url with its params and these newParams. 
+    *   
+    *    Add the SRS parameter from projection -- this is probably
+    *     more eloquently done via a setProjection() method, but this 
+    *     works for now and always.
+    * 
+    * @param {Object} newParams
+    * 
+    * @type String
+    */
+    getFullRequestString:function(newParams) {
+        var projection = this.map.getProjection();
+        this.params.SRS = (projection == "none") ? null : projection;
+
+        return OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(
+                                                    this, arguments);
+    },
+    
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.Layer.WMS"
+});


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/WMS.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/WorldWind.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/WorldWind.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/WorldWind.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,92 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
+ * for the full text of the license. */
+
+
+/**
+ * @class
+ * 
+ * @requires OpenLayers/Layer/Grid.js
+ */
+OpenLayers.Layer.WorldWind = OpenLayers.Class.create();
+OpenLayers.Layer.WorldWind.prototype = 
+  OpenLayers.Class.inherit( OpenLayers.Layer.Grid, {
+    
+    DEFAULT_PARAMS: {
+    },
+
+    /** WorldWind layer is always a base layer
+     * 
+     * @type Boolean
+     */
+    isBaseLayer: true,    
+
+    // LevelZeroTileSizeDegrees
+    lzd: null,
+
+    zoomLevels: null,
+        
+    initialize: function(name, url, lzd, zoomLevels, params, options) {
+        this.lzd = lzd;
+        this.zoomLevels = zoomLevels;
+        var newArguments = new Array();
+        newArguments.push(name, url, params, options);
+        OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);
+        this.params = (params ? params : {});
+        if (params) {
+            OpenLayers.Util.applyDefaults(
+                           this.params, 
+                           this.DEFAULT_PARAMS
+                           );
+        }
+    },
+    addTile:function(bounds,position) {
+        if (this.map.getResolution() <= (this.lzd/512)
+            && this.getZoom() <= this.zoomLevels) {
+            var url = this.getURL(bounds);
+            return new OpenLayers.Tile.Image(this, position, bounds, 
+                                             url, this.tileSize);
+        } else {
+            return new OpenLayers.Tile.Image(this, position, bounds, 
+                       OpenLayers.Util.getImagesLocation() + "blank.gif", 
+                       this.tileSize);
+        }
+    },
+
+    getZoom: function () {
+        var zoom = this.map.getZoom();
+        var extent = this.map.getMaxExtent();
+        zoom = zoom - Math.log(this.maxResolution / (this.lzd/512))/Math.log(2);
+        return zoom;
+    },
+
+    /**
+     * @param {OpenLayers.Bounds} bounds
+     * 
+     * @returns A string with the layer's url and parameters and also the 
+     *           passed-in bounds and appropriate tile size specified as 
+     *           parameters
+     * @type String
+     */
+    getURL: function (bounds) {
+        var zoom = this.getZoom();
+        var extent = this.map.getMaxExtent();
+        var deg = this.lzd/Math.pow(2,this.getZoom());
+        var x = Math.floor((bounds.left - extent.left)/deg);
+        var y = Math.floor((bounds.bottom - extent.bottom)/deg);
+        if (this.map.getResolution() <= (this.lzd/512)
+            && this.getZoom() <= this.zoomLevels) {
+            return this.getFullRequestString(
+              { L: zoom, 
+                X: x,
+                Y: y
+              });
+        } else {
+            return OpenLayers.Util.getImagesLocation() + "blank.gif";
+        }
+
+    },
+
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.Layer.WorldWind"
+});


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/WorldWind.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/Yahoo.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/Yahoo.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/Yahoo.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,286 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
+ * for the full text of the license. */
+
+
+/**
+ * @class
+ * 
+ * @requires OpenLayers/Layer/EventPane.js
+ * @requires OpenLayers/Layer/FixedZoomLevels.js
+ */
+OpenLayers.Layer.Yahoo = OpenLayers.Class.create();
+OpenLayers.Layer.Yahoo.prototype =
+  OpenLayers.Class.inherit( OpenLayers.Layer.EventPane, 
+                            OpenLayers.Layer.FixedZoomLevels, {
+    
+    /** @final @type int */
+    MIN_ZOOM_LEVEL: 0,
+    
+    /** @final @type int */
+    MAX_ZOOM_LEVEL: 15,
+
+    /** Hardcode these resolutions so that they are more closely
+     *   tied with the standard wms projection
+     * 
+     * @final @type Array(float) */
+    RESOLUTIONS: [1.40625,0.703125,0.3515625,0.17578125,0.087890625,0.0439453125,0.02197265625,0.010986328125,0.0054931640625,0.00274658203125,0.001373291015625,0.0006866455078125,0.00034332275390625,0.000171661376953125,0.0000858306884765625,0.00004291534423828125],
+
+    /** @type YahooMapType */
+    type: null,
+
+    /** 
+     * @constructor
+     * 
+     * @param {String} name
+     */
+    initialize: function(name, options) {
+        OpenLayers.Layer.EventPane.prototype.initialize.apply(this, arguments);
+        OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this, 
+                                                                    arguments);
+    },
+    
+    /**
+     * 
+     */
+    loadMapObject:function() {
+        try { //do not crash! 
+            this.mapObject = new YMap(this.div, this.type);
+        } catch(e) {}
+    },
+    
+    
+    /** Overridden from EventPane because we need to remove this yahoo event
+     *   pane which prohibits our drag and drop, and we can only do this 
+     *   once the map has been loaded and centered.
+     * 
+     * @param {OpenLayers.Map} map
+     */
+    setMap: function(map) {
+        OpenLayers.Layer.EventPane.prototype.setMap.apply(this, arguments);
+
+        this.map.events.register("moveend", this, this.fixYahooEventPane);
+    },
+
+    /** The map has been centered, so the mysterious yahoo eventpane has been
+     *   added. we remove it so that it doesnt mess with *our* event pane.
+     * 
+     * @private
+     */
+    fixYahooEventPane: function() {
+        var yahooEventPane = $("ygddfdiv");
+        if (yahooEventPane != null) {
+            if (yahooEventPane.parentNode != null)
+                yahooEventPane.parentNode.removeChild(yahooEventPane);
+
+            this.map.events.unregister("moveend", this, 
+                                       this.fixYahooEventPane);
+        }
+    },
+
+    /** 
+     * @return String with information on why layer is broken, how to get
+     *          it working.
+     * @type String
+     */
+    getWarningHTML:function() {
+
+        var html = "";
+        html += "The Yahoo Layer was unable to load correctly.<br>";
+        html += "<br>";
+        html += "To get rid of this message, select a new BaseLayer "
+        html += "in the layer switcher in the upper-right corner.<br>";
+        html += "<br>";
+        html += "Most likely, this is because the Yahoo library";
+        html += " script was either not correctly included.<br>";
+        html += "<br>";
+        html += "Developers: For help getting this working correctly, ";
+        html += "<a href='http://trac.openlayers.org/wiki/Yahoo' "
+        html +=  "target='_blank'>";
+        html +=     "click here";
+        html += "</a>";
+
+        return html;
+    },
+
+  /********************************************************/
+  /*                                                      */
+  /*             Translation Functions                    */
+  /*                                                      */
+  /*    The following functions translate GMaps and OL    */ 
+  /*     formats for Pixel, LonLat, Bounds, and Zoom      */
+  /*                                                      */
+  /********************************************************/
+
+
+  //
+  // TRANSLATION: MapObject Zoom <-> OpenLayers Zoom
+  //
+  
+    /**
+     * @param {int} gZoom
+     * 
+     * @returns An OpenLayers Zoom level, translated from the passed in gZoom
+     *          Returns null if null value is passed in
+     * @type int
+     */
+    getOLZoomFromMapObjectZoom: function(moZoom) {
+        var zoom = null;
+        if (moZoom != null) {
+            zoom = OpenLayers.Layer.FixedZoomLevels.prototype.getOLZoomFromMapObjectZoom.apply(this, [moZoom]);
+            zoom = 18 - zoom;
+        }
+        return zoom;
+    },
+    
+    /**
+     * @param {int} olZoom
+     * 
+     * @returns A MapObject level, translated from the passed in olZoom
+     *          Returns null if null value is passed in
+     * @type int
+     */
+    getMapObjectZoomFromOLZoom: function(olZoom) {
+        var zoom = null; 
+        if (olZoom != null) {
+            zoom = OpenLayers.Layer.FixedZoomLevels.prototype.getMapObjectZoomFromOLZoom.apply(this, [olZoom]);
+            zoom = 18 - zoom;
+        }
+        return zoom;
+    },
+
+    /************************************
+     *                                  *
+     *   MapObject Interface Controls   *
+     *                                  *
+     ************************************/
+
+
+  // Get&Set Center, Zoom
+
+    /** Set the mapObject to the specified center and zoom
+     * 
+     * @param {Object} center MapObject LonLat format
+     * @param {int} zoom MapObject zoom format
+     */
+    setMapObjectCenter: function(center, zoom) {
+        this.mapObject.drawZoomAndCenter(center, zoom); 
+    },
+   
+    /**
+     * @returns the mapObject's current center in Map Object format
+     * @type Object
+     */
+    getMapObjectCenter: function() {
+        return this.mapObject.getCenterLatLon();
+    },
+
+    /** 
+     * @returns the mapObject's current zoom, in Map Object format
+     * @type int
+     */
+    getMapObjectZoom: function() {
+        return this.mapObject.getZoomLevel();
+    },
+
+
+  // LonLat - Pixel Translation
+  
+    /** 
+     * @param {Object} moPixel MapObject Pixel format
+     * 
+     * @returns MapObject LonLat translated from MapObject Pixel
+     * @type Object
+     */
+    getMapObjectLonLatFromMapObjectPixel: function(moPixel) {
+        return this.mapObject.convertXYLatLon(moPixel);
+    },
+
+    /** 
+     * @param {Object} moPixel MapObject Pixel format
+     * 
+     * @returns MapObject Pixel translated from MapObject LonLat
+     * @type Object
+     */
+    getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {
+        return this.mapObject.convertLatLonXY(moLonLat);
+    },
+
+
+    /************************************
+     *                                  *
+     *       MapObject Primitives       *
+     *                                  *
+     ************************************/
+
+
+  // LonLat
+    
+    /**
+     * @param {Object} moLonLat MapObject LonLat format
+     * 
+     * @returns Longitude of the given MapObject LonLat
+     * @type float
+     */
+    getLongitudeFromMapObjectLonLat: function(moLonLat) {
+        return moLonLat.Lon;
+    },
+
+    /**
+     * @param {Object} moLonLat MapObject LonLat format
+     * 
+     * @returns Latitude of the given MapObject LonLat
+     * @type float
+     */
+    getLatitudeFromMapObjectLonLat: function(moLonLat) {
+        return moLonLat.Lat;
+    },
+
+    /**
+     * @param {int} lon float
+     * @param {int} lat float
+     * 
+     * @returns MapObject LonLat built from lon and lat params
+     * @type Object
+     */
+    getMapObjectLonLatFromLonLat: function(lon, lat) {
+        return new YGeoPoint(lat, lon);
+    },
+
+  // Pixel
+    
+    /** 
+     * @param {Object} moPixel MapObject Pixel format
+     * 
+     * @returns X value of the MapObject Pixel
+     * @type int
+     */
+    getXFromMapObjectPixel: function(moPixel) {
+        return moPixel.x;
+    },
+
+    /** 
+     * @param {Object} moPixel MapObject Pixel format
+     * 
+     * @returns Y value of the MapObject Pixel
+     * @type int
+     */
+    getYFromMapObjectPixel: function(moPixel) {
+        return moPixel.y;
+    },
+
+    /** 
+     * @param {int} x
+     * @param {int} y
+     * 
+     * @returns MapObject Pixel from x and y parameters
+     * @type Object
+     */
+    getMapObjectPixelFromXY: function(x, y) {
+        return new YCoordPoint(x, y);
+    },
+
+
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.Layer.Yahoo"
+});


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer/Yahoo.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,563 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
+ * for the full text of the license. */
+
+
+/**
+ * @class
+ */
+OpenLayers.Layer = OpenLayers.Class.create();
+OpenLayers.Layer.prototype = {
+
+    /** @type String */
+    id: null,
+
+    /** @type String */
+    name: null,
+
+    /** @type DOMElement */
+    div: null,
+
+    /** This variable is set when the layer is added to the map, via the 
+     *  accessor function setMap()
+     * 
+     * @type OpenLayers.Map */
+    map: null,
+    
+    /** Whether or not the layer is a base layer. This should be set 
+     *   individually by all subclasses. 
+     *   Default is false
+     * 
+     * @type Boolean
+     */
+    isBaseLayer: false,
+ 
+    /** asserts whether or not the layer's images have an alpha channel 
+     * 
+     * @type boolean */
+    alpha: false,
+
+    /** should the layer's name appear in the layer switcher?
+     * 
+     * @type boolean */
+    displayInLayerSwitcher: true,
+
+    /** Whether or not the layer should be displayed in the map
+     * 
+     * @type Boolean
+     */
+    visibility: true,
+
+    /** Whether or not the map's current resolution is within this layer's
+     *   min/max range -- this is set in map's setCenter() whenever zoom
+     *   changes
+     * 
+     * @type Boolean
+     */
+    inRange: false,
+
+  // OPTIONS
+
+    /** @type Array */
+    options: null,
+
+    /** @type String */
+    projection: null,    
+    
+    /** @type String */
+    units: null,
+
+    /** @type Array */
+    scales: null,
+
+    /** @type Array */
+    resolutions: null,
+    
+    /** @type OpenLayers.Bounds */
+    maxExtent: null,
+    
+    /** @type OpenLayers.Bounds */
+    minExtent: null,
+    
+    /** @type float */
+    maxResolution: null,
+
+    /** @type float */
+    minResolution: null,
+
+    /** @type int */
+    numZoomLevels: null,
+   
+    /** @type float */
+    minScale: null,
+    
+    /** @type float */
+    maxScale: null,
+
+    /** @type Boolean */
+    displayOutsideMaxExtent: false,
+    
+    
+    /**
+     * @constructor
+     * 
+     * @param {String} name
+     * @param {Object} options Hashtable of extra options to tag onto the layer
+     */
+    initialize: function(name, options) {
+        //store a copy of the custom options for later cloning
+        this.options = OpenLayers.Util.extend(new Object(), options);
+        
+        //add options to layer
+        OpenLayers.Util.extend(this, this.options);
+
+        this.name = name;
+        
+        this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
+                
+        if (this.div == null) {
+            this.div = OpenLayers.Util.createDiv();
+            this.div.style.width = "100%";
+            this.div.style.height = "100%";
+            this.div.id = this.id;
+        }
+    },
+    
+    /**
+     * Destroy is a destructor: this is to alleviate cyclic references which
+     * the Javascript garbage cleaner can not take care of on its own.
+     */
+    destroy: function() {
+        if (this.map != null) {
+            this.map.removeLayer(this);
+        }
+        this.map = null;
+        this.name = null;
+        this.div = null;
+        this.options = null;
+    },
+    
+   /**
+    * @returns An exact clone of this OpenLayers.Layer
+    * @type OpenLayers.Layer
+    */
+    clone: function (obj) {
+        
+        if (obj == null) {
+            obj = new OpenLayers.Layer(this.name, this.options);
+        } 
+        
+        // catch any randomly tagged-on properties
+        OpenLayers.Util.applyDefaults(obj, this);
+        
+        // a cloned layer should never have its map property set
+        //  because it has not been added to a map yet. 
+        obj.map = null;
+        
+        return obj;
+    },
+    
+    /** 
+     * @param {String} newName
+     */
+    setName: function(newName) {
+        this.name = newName;
+        if (this.map != null)
+            this.map.events.triggerEvent("changelayer");
+    },    
+    
+   /**
+    * @param {Object} newOptions
+    */
+    addOptions: function (newOptions) {
+        
+        // update our copy for clone
+        OpenLayers.Util.extend(this.options, newOptions);
+
+        // add new options to this
+        OpenLayers.Util.extend(this, newOptions);
+    },
+    
+    /**
+     * 
+     */
+    onMapResize: function() {
+        //this function can be implemented by subclasses  
+    },
+
+    /**
+     * @param {OpenLayers.Bounds} bound
+     * @param {Boolean} zoomChanged tells when zoom has changed, as layers 
+     *                   have to do some init work in that case.
+     * @param {Boolean} dragging
+     */
+    moveTo:function(bounds, zoomChanged, dragging) {
+        var display = this.visibility;
+        if (!this.isBaseLayer) {
+            display = display && this.inRange;
+        }
+        this.display(display);
+    },
+
+    /** Set the map property for the layer. This is done through an accessor
+     *   so that subclasses can override this and take special action once 
+     *   they have their map variable set. 
+     * 
+     *  Here we take care to bring over any of the necessary default properties
+     *   from the map. 
+     * 
+     * @param {OpenLayers.Map} map
+     */
+    setMap: function(map) {
+        this.map = map;
+        
+        // grab some essential layer data from the map if it hasn't already
+        //  been set
+        this.maxExtent = this.maxExtent || this.map.maxExtent;
+        this.projection = this.projection || this.map.projection;
+        this.units = this.units || this.map.units;
+        
+        this.initResolutions();
+        
+        if (!this.isBaseLayer) {
+            this.inRange = this.calculateInRange();
+        }    
+    },
+  
+    /**
+    * @returns Whether or not the layer should be displayed (if in range)
+    * @type Boolean
+    */
+    getVisibility: function() {
+        return this.visibility;
+    },
+
+    /** Set the visibility flag for the layer and hide/show&redraw accordingly. 
+     *   Fire event unless otherwise specified
+     * 
+     * Note that visibility is no longer simply whether or not the layer's
+     *  style.display is set to "block". Now we store a 'visibility' state 
+     *  property on the layer class, this allows us to remember whether or not
+     *  we *desire* for a layer to be visible. In the case where the map's 
+     *  resolution is out of the layer's range, this desire may be subverted.
+     *  
+     * @param {Boolean} visible Whether or not to display the layer 
+     *                          (if in range)
+     * @param {Boolean} noEvent
+     */
+    setVisibility: function(visibility, noEvent) {
+        if (visibility != this.visibility) {
+            this.visibility = visibility;
+            this.display(visibility);
+            if (this.map != null) {
+                var extent = this.map.getExtent();
+                if (extent != null) {
+                    this.moveTo(extent, true);
+                }
+            }
+            if ((this.map != null) && 
+                ((noEvent == null) || (noEvent == false))) {
+                this.map.events.triggerEvent("changelayer");
+            }
+        }
+    },
+
+    /** Hide or show the Layer
+     * 
+     * @param {Boolean} display
+     */
+    display: function(display) {
+        if (display != (this.div.style.display != "none")) {
+            this.div.style.display = (display) ? "block" : "none";
+        }
+    },
+
+    /**
+     * @returns Whether or not the layer is displayable at the current map's
+     *          current resolution
+     * @type Boolean
+     */
+    calculateInRange: function() {
+        var inRange = false;
+        if (this.map) {
+            var resolution = this.map.getResolution();
+            inRange = ( (resolution >= this.minResolution) &&
+                        (resolution <= this.maxResolution) );
+        }
+        return inRange;
+    },
+
+    /** 
+     * @param {Boolean} isBaseLayer 
+     */
+    setIsBaseLayer: function(isBaseLayer) {
+        this.isBaseLayer = isBaseLayer;
+        if (this.map != null) {
+            this.map.events.triggerEvent("changelayer");
+        }
+    },
+
+  /********************************************************/
+  /*                                                      */
+  /*                 Baselayer Functions                  */
+  /*                                                      */
+  /********************************************************/
+  
+    /** This method's responsibility is to set up the 'resolutions' array 
+     *   for the layer -- this array is what the layer will use to interface
+     *   between the zoom levels of the map and the resolution display of the
+     *   layer.
+     * 
+     *  The user has several options that determine how the array is set up.
+     *  
+     *  For a detailed explanation, see the following wiki from the 
+     *   openlayers.org homepage:
+     * 
+     *  http://trac.openlayers.org/wiki/SettingZoomLevels
+     * 
+     * @private
+     */
+    initResolutions: function() {
+
+        // These are the relevant options which are used for calculating 
+        //  resolutions information.
+        //
+        var props = new Array(
+          'projection', 'units',
+          'scales', 'resolutions',
+          'maxScale', 'minScale', 
+          'maxResolution', 'minResolution', 
+          'minExtent', 'maxExtent',
+          'numZoomLevels', 'maxZoomLevel'
+        );
+
+        // First we create a new object where we will store all of the 
+        //  resolution-related properties that we find in either the layer's
+        //  'options' array or from the map.
+        //
+        var confProps = new Object();        
+        for(var i=0; i < props.length; i++) {
+            var property = props[i];
+            confProps[property] = this.options[property] || this.map[property];
+        }
+
+        // If numZoomLevels hasn't been set and the maxZoomLevel *has*, 
+        //  then use maxZoomLevel to calculate numZoomLevels
+        //
+        if ( (!confProps.numZoomLevels) && (confProps.maxZoomLevel) ) {
+            confProps.numZoomLevels = confProps.maxZoomLevel + 1;
+        }
+
+        // First off, we take whatever hodge-podge of values we have and 
+        //  calculate/distill them down into a resolutions[] array
+        //
+        if ((confProps.scales != null) || (confProps.resolutions != null)) {
+          //preset levels
+            if (confProps.scales != null) {
+                confProps.resolutions = new Array();
+                for(var i = 0; i < confProps.scales.length; i++) {
+                    var scale = confProps.scales[i];
+                    confProps.resolutions[i] = 
+                       OpenLayers.Util.getResolutionFromScale(scale, 
+                                                              confProps.units);
+                }
+            }
+            confProps.numZoomLevels = confProps.resolutions.length;
+
+        } else {
+          //maxResolution and numZoomLevels based calculation
+            
+            confProps.resolutions = new Array();
+            
+            // determine maxResolution
+            if (confProps.minScale) {
+                confProps.maxResolution = 
+                    OpenLayers.Util.getResolutionFromScale(confProps.minScale, 
+                                                           confProps.units);
+            } else if (confProps.maxResolution == "auto") {
+                var viewSize = this.map.getSize();
+                var wRes = confProps.maxExtent.getWidth() / viewSize.w;
+                var hRes = confProps.maxExtent.getHeight()/ viewSize.h;
+                confProps.maxResolution = Math.max(wRes, hRes);
+            } 
+
+            // determine minResolution
+            if (confProps.maxScale != null) {           
+                confProps.minResolution = 
+                    OpenLayers.Util.getResolutionFromScale(confProps.maxScale);
+            } else if ( (confProps.minResolution == "auto") && 
+                        (confProps.minExtent != null) ) {
+                var viewSize = this.map.getSize();
+                var wRes = confProps.minExtent.getWidth() / viewSize.w;
+                var hRes = confProps.minExtent.getHeight()/ viewSize.h;
+                confProps.minResolution = Math.max(wRes, hRes);
+            } 
+
+            // determine numZoomLevels
+            if (confProps.minResolution != null) {
+                var ratio = confProps.maxResolution / confProps.minResolution;
+                confProps.numZoomLevels = 
+                    Math.floor(Math.log(ratio) / Math.log(2)) + 1;
+            }
+            
+            // now we have numZoomLevels and maxResolution, 
+            //  we can populate the resolutions array
+            for (var i=0; i < confProps.numZoomLevels; i++) {
+                var res = confProps.maxResolution / Math.pow(2, i)
+                confProps.resolutions.push(res);
+            }    
+        }
+        
+        //sort resolutions array ascendingly
+        //
+        confProps.resolutions.sort( function(a, b) { return(b-a); } );
+
+        // now set our newly calculated values back to the layer 
+        //  Note: We specifically do *not* set them to layer.options, which we 
+        //        will preserve as it was when we added this layer to the map. 
+        //        this way cloned layers reset themselves to new map div 
+        //        dimensions)
+        //
+
+        this.resolutions = confProps.resolutions;
+        this.maxResolution = confProps.resolutions[0];
+        var lastIndex = confProps.resolutions.length - 1;
+        this.minResolution = confProps.resolutions[lastIndex];
+        
+        this.scales = new Array();
+        for(var i = 0; i < confProps.resolutions.length; i++) {
+            this.scales[i] = 
+               OpenLayers.Util.getScaleFromResolution(confProps.resolutions[i], 
+                                                      confProps.units);
+        }
+        this.minScale = this.scales[0];
+        this.maxScale = this.scales[this.scales.length - 1];
+        
+        this.numZoomLevels = confProps.numZoomLevels;
+    },
+
+    /**
+     * @returns The currently selected resolution of the map, taken from the
+     *          resolutions array, indexed by current zoom level.
+     * @type float
+     */
+    getResolution: function() {
+        var zoom = this.map.getZoom();
+        return this.resolutions[zoom];
+    },
+
+    /** 
+     * @returns A Bounds object which represents the lon/lat bounds of the 
+     *          current viewPort.
+     * @type OpenLayers.Bounds
+     */
+    getExtent: function() {
+        // just use stock map calculateBounds function -- passing no arguments
+        //  means it will user map's current center & resolution
+        //
+        return this.map.calculateBounds();
+    },
+
+    /**
+     * @param {OpenLayers.Bounds} bounds
+     *
+     * @returns The index of the zoomLevel (entry in the resolutions array) 
+     *           that still contains the passed-in extent. We do this by 
+     *           calculating the ideal resolution for the given exteng (based
+     *           on the map size) and then find the smallest resolution that 
+     *           is greater than this ideal resolution.
+     * @type int
+     */
+    getZoomForExtent: function(extent) {
+        var viewSize = this.map.getSize();
+        var idealResolution = Math.max( extent.getWidth()  / viewSize.w,
+                                        extent.getHeight() / viewSize.h );
+
+        return this.getZoomForResolution(idealResolution);
+    },
+    
+    /**
+     * @param {float} resolution
+     *
+     * @returns The index of the zoomLevel (entry in the resolutions array) 
+     *           that is the smallest resolution that is greater than the 
+     *           passed-in resolution.
+     * @type int
+     */
+    getZoomForResolution: function(resolution) {
+        
+        for(var i=1; i < this.resolutions.length; i++) {
+            if ( this.resolutions[i] < resolution) {
+                break;
+            }
+        }
+        return (i - 1);
+    },
+    
+    /**
+     * @param {OpenLayers.Pixel} viewPortPx
+     *
+     * @returns An OpenLayers.LonLat which is the passed-in view port
+     *          OpenLayers.Pixel, translated into lon/lat by the layer
+     * @type OpenLayers.LonLat
+     */
+    getLonLatFromViewPortPx: function (viewPortPx) {
+        var lonlat = null;
+        if (viewPortPx != null) {
+            var size = this.map.getSize();
+            var center = this.map.getCenter();
+            var res  = this.map.getResolution();
+        
+            var delta_x = viewPortPx.x - (size.w / 2);
+            var delta_y = viewPortPx.y - (size.h / 2);
+            
+            lonlat = new OpenLayers.LonLat(center.lon + delta_x * res ,
+                                         center.lat - delta_y * res); 
+        }
+        return lonlat;
+    },
+
+    /**
+     * @param {OpenLayers.LonLat} lonlat
+     *
+     * @returns An OpenLayers.Pixel which is the passed-in OpenLayers.LonLat, 
+     *          translated into view port pixels
+     * @type OpenLayers.Pixel
+     */
+    getViewPortPxFromLonLat: function (lonlat) {
+        var px = null; 
+        if (lonlat != null) {
+            var resolution = this.map.getResolution();
+            var extent = this.map.getExtent();
+            px = new OpenLayers.Pixel(
+                           Math.round(1/resolution * (lonlat.lon - extent.left)),
+                           Math.round(1/resolution * (extent.top - lonlat.lat))
+                           );    
+        }
+        return px;
+    },
+    
+    /**
+     * Sets the opacity for the entire layer (all images)
+     * @param {Float} opacity
+     */
+    setOpacity: function(opacity) {
+        this.opacity = opacity;
+        for(var i=0; i<this.div.childNodes.length; ++i) {
+            var element = this.div.childNodes[i];
+            OpenLayers.Util.modifyDOMElement(element, null, null, null, 
+                                             null, null, null, opacity);
+        }
+    },
+
+    /**
+    * @param {int} zIdx
+    * @private
+    */    
+    setZIndex: function (zIdx) {
+        this.div.style.zIndex = zIdx;
+    },
+
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.Layer"
+};


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Layer.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Map.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Map.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Map.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,1188 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
+ * for the full text of the license. */
+
+
+/**
+ * @class
+ * 
+ * @requires OpenLayers/Util.js
+ * @requires OpenLayers/Events.js
+ */
+OpenLayers.Map = OpenLayers.Class.create();
+OpenLayers.Map.TILE_WIDTH = 256;
+OpenLayers.Map.TILE_HEIGHT = 256;
+OpenLayers.Map.prototype = {
+    
+    /** base z-indexes for different classes of thing 
+     * 
+     * @type Object
+     */
+    Z_INDEX_BASE: { BaseLayer: 100, Overlay: 325, Popup: 750, Control: 1000 },
+
+    /** supported application event types
+     * 
+     * @type Array */
+    EVENT_TYPES: [ 
+        "addlayer", "removelayer", "changelayer", "movestart", "move", 
+        "moveend", "zoomend", "popupopen", "popupclose",
+        "addmarker", "removemarker", "clearmarkers", "mouseover",
+        "mouseout", "mousemove", "dragstart", "drag", "dragend",
+        "changebaselayer"],
+
+    /** @type OpenLayers.Events */
+    events: null,
+
+    /** the div that our map lives in
+     * 
+     * @type DOMElement */
+    div: null,
+
+    /** Size of the main div (this.div)
+     * 
+     * @type OpenLayers.Size */
+    size: null,
+    
+    /** @type HTMLDivElement  */
+    viewPortDiv: null,
+
+    /** The lonlat at which the later container was re-initialized (on-zoom)
+     * @type OpenLayers.LonLat */
+    layerContainerOrigin: null,
+
+    /** @type HTMLDivElement */
+    layerContainerDiv: null,
+
+    /** ordered list of layers in the map
+     * 
+     * @type Array(OpenLayers.Layer)
+     */
+    layers: null,
+
+    /** @type Array(OpenLayers.Control) */
+    controls: null,
+
+    /** @type Array(OpenLayers.Popup) */
+    popups: null,
+
+    /** The currently selected base layer - this determines min/max zoom level, 
+     *  projection, etc.
+     * 
+     * @type OpenLayers.Layer */
+    baseLayer: null,
+    
+    /** @type OpenLayers.LonLat */
+    center: null,
+
+    /** @type int */
+    zoom: 0,    
+
+  // Options
+
+    /** @type OpenLayers.Size */
+    tileSize: null,
+
+    /** @type String */
+    projection: "EPSG:4326",    
+        
+    /** @type String */
+    units: 'degrees',
+
+    /** default max is 360 deg / 256 px, which corresponds to
+     *    zoom level 0 on gmaps
+     * 
+     * @type float */
+    maxResolution: 1.40625,
+
+    /** @type float */
+    minResolution: null,
+
+    /** @type float */
+    maxScale: null,
+
+    /** @type float */
+    minScale: null,
+
+    /** @type OpenLayers.Bounds */
+    maxExtent: null,
+    
+    /** @type OpenLayers.Bounds */
+    minExtent: null,
+    
+    /** @type int */
+    numZoomLevels: 16,
+
+    /** @type string */
+    theme: null,
+
+    /**
+     * @constructor
+     * 
+     * @param {DOMElement} div
+     * @param {Object} options Hashtable of extra options to tag onto the map
+     */    
+    initialize: function (div, options) {
+
+        this.div = div = $(div);
+
+        // the viewPortDiv is the outermost div we modify
+        var id = div.id + "_OpenLayers_ViewPort";
+        this.viewPortDiv = OpenLayers.Util.createDiv(id, null, null, null,
+                                                     "relative", null,
+                                                     "hidden");
+        this.viewPortDiv.style.width = "100%";
+        this.viewPortDiv.style.height = "100%";
+        this.viewPortDiv.className = "olMapViewport";
+        this.div.appendChild(this.viewPortDiv);
+
+        // the layerContainerDiv is the one that holds all the layers
+        id = div.id + "_OpenLayers_Container";
+        this.layerContainerDiv = OpenLayers.Util.createDiv(id);
+        this.layerContainerDiv.style.zIndex=this.Z_INDEX_BASE['Popup']-1;
+        
+        this.viewPortDiv.appendChild(this.layerContainerDiv);
+
+        this.events = new OpenLayers.Events(this, div, this.EVENT_TYPES);
+
+        this.updateSize();
+ 
+    // Because Mozilla does not support the "resize" event for elements other
+    //  than "window", we need to put a hack here. 
+    // 
+        if (navigator.appName.contains("Microsoft")) {
+            // If IE, register the resize on the div
+            this.events.register("resize", this, this.updateSize);
+        } else {
+            // Else updateSize on catching the window's resize
+            //  Note that this is ok, as updateSize() does nothing if the 
+            //  map's size has not actually changed.
+            OpenLayers.Event.observe(window, 'resize', 
+                          this.updateSize.bindAsEventListener(this));
+        }
+        
+        //set the default options
+        this.setOptions(options);
+        
+        var cssNode = document.createElement('link');
+        cssNode.setAttribute('rel', 'stylesheet');
+        cssNode.setAttribute('type', 'text/css');
+        cssNode.setAttribute('href', this.theme);
+        document.getElementsByTagName('head')[0].appendChild(cssNode); 
+
+        this.layers = [];
+        
+        if (this.controls == null) {
+            if (OpenLayers.Control != null) { // running full or lite?
+                this.controls = [ new OpenLayers.Control.MouseDefaults(),
+                                  new OpenLayers.Control.PanZoom(),
+                                  new OpenLayers.Control.ArgParser()
+                                ];
+            } else {
+                this.controls = [];
+            }
+        }
+
+        for(var i=0; i < this.controls.length; i++) {
+            this.addControlToMap(this.controls[i]);
+        }
+
+        this.popups = new Array();
+
+        // always call map.destroy()
+        OpenLayers.Event.observe(window, 
+                      'unload', 
+                      this.destroy.bindAsEventListener(this));
+
+    },
+
+    /**
+    * @private
+    */
+    destroy:function() {
+        if (this.layers != null) {
+            for(var i=0; i< this.layers.length; i++) {
+                this.layers[i].destroy();
+            } 
+            this.layers = null;
+        }
+        if (this.controls != null) {
+            for(var i=0; i< this.controls.length; i++) {
+                this.controls[i].destroy();
+            } 
+            this.controls = null;
+        }
+    },
+
+    /**
+     * @private
+     * 
+     * @param {Object} options Hashtable of options to tag to the map
+     */
+    setOptions: function(options) {
+
+        // Simple-type defaults are set in class definition. 
+        //  Now set complex-type defaults 
+        this.tileSize = new OpenLayers.Size(OpenLayers.Map.TILE_WIDTH,
+                                            OpenLayers.Map.TILE_HEIGHT);
+        
+        this.maxExtent = new OpenLayers.Bounds(-180, -90, 180, 90);
+
+        this.theme = OpenLayers._getScriptLocation() + 
+                             'theme/default/style.css'; 
+
+        // now add the options declared by the user
+        //  (these will override defaults)
+        OpenLayers.Util.extend(this, options);
+    },
+
+    /**
+     * @type OpenLayers.Size
+     */
+     getTileSize: function() {
+         return this.tileSize;
+     },
+
+  /********************************************************/
+  /*                                                      */
+  /*           Layers, Controls, Popup Functions          */
+  /*                                                      */
+  /*     The following functions deal with adding and     */
+  /*        removing Layers, Controls, and Popups         */
+  /*                to and from the Map                   */
+  /*                                                      */
+  /********************************************************/         
+
+    /**
+     * @param {String} name
+     * 
+     * @returns The Layer with the corresponding id from the map's 
+     *           layer collection, or null if not found.
+     * @type OpenLayers.Layer
+     */
+    getLayer: function(id) {
+        var foundLayer = null;
+        for (var i = 0; i < this.layers.length; i++) {
+            var layer = this.layers[i];
+            if (layer.id == id) {
+                foundLayer = layer;
+            }
+        }
+        return foundLayer;
+    },
+
+    /**
+    * @param {OpenLayers.Layer} layer
+    * @param {int} zIdx
+    * @private
+    */    
+    setLayerZIndex: function (layer, zIdx) {
+        layer.setZIndex(
+            this.Z_INDEX_BASE[layer.isBaseLayer ? 'BaseLayer' : 'Overlay']
+            + zIdx * 5 );
+    },
+
+    /**
+    * @param {OpenLayers.Layer} layer
+    */    
+    addLayer: function (layer) {
+        layer.div.style.overflow = "";
+        this.setLayerZIndex(layer, this.layers.length);
+
+        if (layer.isFixed) {
+            this.viewPortDiv.appendChild(layer.div);
+        } else {
+            this.layerContainerDiv.appendChild(layer.div);
+        }
+        this.layers.push(layer);
+        layer.setMap(this);
+
+        if (layer.isBaseLayer)  {
+            if (this.baseLayer == null) {
+                // set the first baselaye we add as the baselayer
+                this.setBaseLayer(layer);
+            } else {
+                layer.setVisibility(false);
+            }
+        } else {
+            if (this.getCenter() != null) {
+                layer.moveTo(this.getExtent(), true);   
+            }
+        }
+
+        this.events.triggerEvent("addlayer");
+    },
+
+    /**
+    * @param {Array(OpenLayers.Layer)} layers
+    */    
+    addLayers: function (layers) {
+        for (var i = 0; i <  layers.length; i++) {
+            this.addLayer(layers[i]);
+        }
+    },
+
+    /** Removes a layer from the map by removing its visual element (the 
+     *   layer.div property), then removing it from the map's internal list 
+     *   of layers, setting the layer's map property to null. 
+     * 
+     *   a "removelayer" event is triggered.
+     * 
+     *   very worthy of mention is that simply removing a layer from a map
+     *   will not cause the removal of any popups which may have been created
+     *   by the layer. this is due to the fact that it was decided at some
+     *   point that popups would not belong to layers. thus there is no way 
+     *   for us to know here to which layer the popup belongs.
+     *    
+     *     A simple solution to this is simply to call destroy() on the layer.
+     *     the default OpenLayers.Layer class's destroy() function
+     *     automatically takes care to remove itself from whatever map it has
+     *     been attached to. 
+     * 
+     *     The correct solution is for the layer itself to register an 
+     *     event-handler on "removelayer" and when it is called, if it 
+     *     recognizes itself as the layer being removed, then it cycles through
+     *     its own personal list of popups, removing them from the map.
+     * 
+     * @param {OpenLayers.Layer} layer
+     */
+    removeLayer: function(layer) {
+        if (layer.isFixed) {
+            this.viewPortDiv.removeChild(layer.div);
+        } else {
+            this.layerContainerDiv.removeChild(layer.div);
+        }
+        layer.map = null;
+        OpenLayers.Util.removeItem(this.layers, layer);
+
+        // if we removed the base layer, need to set a new one
+        if (this.baseLayer == layer) {
+            this.baseLayer = null;
+            for(i=0; i < this.layers.length; i++) {
+                var iLayer = this.layers[i];
+                if (iLayer.isBaseLayer) {
+                    this.setBaseLayer(iLayer);
+                    break;
+                }
+            }
+        }
+        this.events.triggerEvent("removelayer");
+    },
+
+    /**
+    * @returns The number of layers attached to the map.
+    * @type int
+    */
+    getNumLayers: function () {
+        return this.layers.length;
+    },
+
+    /** 
+    * @returns The current (zero-based) index of the given layer in the map's
+    *     layer stack. Returns -1 if the layer isn't on the map.
+    *
+    * @param {OpenLayers.Layer} layer
+    * @type int
+    */
+    getLayerIndex: function (layer) {
+        return OpenLayers.Util.indexOf(this.layers, layer);
+    },
+    
+    /** Move the given layer to the specified (zero-based) index in the layer
+    *     list, changing its z-index in the map display. Use
+    *     map.getLayerIndex() to find out the current index of a layer. Note
+    *     that this cannot (or at least should not) be effectively used to
+    *     raise base layers above overlays.
+    *
+    * @param {OpenLayers.Layer} layer
+    * @param {int} idx
+    */
+    setLayerIndex: function (layer, idx) {
+        var base = this.getLayerIndex(layer);
+        if (idx < 0) 
+            idx = 0;
+        else if (idx > this.layers.length)
+            idx = this.layers.length;
+        if (base != idx) {
+            this.layers.splice(base, 1);
+            this.layers.splice(idx, 0, layer);
+            for (var i = 0; i < this.layers.length; i++)
+                this.setLayerZIndex(this.layers[i], i);
+            this.events.triggerEvent("changelayer");
+        }
+    },
+
+    /** Change the index of the given layer by delta. If delta is positive, 
+    *     the layer is moved up the map's layer stack; if delta is negative,
+    *     the layer is moved down.  Again, note that this cannot (or at least
+    *     should not) be effectively used to raise base layers above overlays.
+    *
+    * @param {OpenLayers.Layer} layer
+    * @param {int} idx
+    */
+    raiseLayer: function (layer, delta) {
+        var idx = this.getLayerIndex(layer) + delta;
+        this.setLayerIndex(layer, idx);
+    },
+    
+    /** Allows user to specify one of the currently-loaded layers as the Map's
+     *   new base layer.
+     * 
+     * @param {OpenLayers.Layer} newBaseLayer
+     * @param {Boolean} noEvent
+     */
+    setBaseLayer: function(newBaseLayer, noEvent) {
+        var oldBaseLayer = this.baseLayer;
+
+        if (newBaseLayer != oldBaseLayer) {
+          
+            // is newBaseLayer an already loaded layer?
+            if (OpenLayers.Util.indexOf(this.layers, newBaseLayer) != -1) {
+
+                // make the old base layer invisible 
+                if (oldBaseLayer != null) {
+                    oldBaseLayer.setVisibility(false, noEvent);
+                }
+
+                // set new baselayer and make it visible
+                this.baseLayer = newBaseLayer;
+                this.baseLayer.setVisibility(true, noEvent);
+
+                //redraw all layers
+                var center = this.getCenter();
+                if (center != null) {
+                    if (oldBaseLayer == null) {
+                        this.setCenter(center);            
+                    } else {
+                        this.zoomToExtent(oldBaseLayer.getExtent());
+                    }
+                }
+
+                if ((noEvent == null) || (noEvent == false)) {
+                    this.events.triggerEvent("changebaselayer");
+                }
+            }        
+        }
+    },
+
+    /**
+    * @param {OpenLayers.Control} control
+    * @param {OpenLayers.Pixel} px
+    */    
+    addControl: function (control, px) {
+        this.controls.push(control);
+        this.addControlToMap(control, px);
+    },
+
+    /**
+     * @private
+     * 
+     * @param {OpenLayers.Control} control
+     * @param {OpenLayers.Pixel} px
+     */    
+    addControlToMap: function (control, px) {
+        control.setMap(this);
+        var div = control.draw(px);
+        if (div) {
+            div.style.zIndex = this.Z_INDEX_BASE['Control'] +
+                                this.controls.length;
+            this.viewPortDiv.appendChild( div );
+        }
+    },
+    
+    /** 
+    * @param {OpenLayers.Popup} popup
+    * @param {Boolean} exclusive If true, closes all other popups first
+    */
+    addPopup: function(popup, exclusive) {
+
+        if (exclusive) {
+            //remove all other popups from screen
+            for(var i=0; i < this.popups.length; i++) {
+                this.removePopup(this.popups[i]);
+            }
+        }
+
+        popup.map = this;
+        this.popups.push(popup);
+        var popupDiv = popup.draw();
+        if (popupDiv) {
+            popupDiv.style.zIndex = this.Z_INDEX_BASE['Popup'] +
+                                    this.popups.length;
+            this.layerContainerDiv.appendChild(popupDiv);
+        }
+    },
+    
+    /** 
+    * @param {OpenLayers.Popup} popup
+    */
+    removePopup: function(popup) {
+        OpenLayers.Util.removeItem(this.popups, popup);
+        if (popup.div) {
+            try { this.layerContainerDiv.removeChild(popup.div); }
+            catch (e) { } // Popups sometimes apparently get disconnected
+                      // from the layerContainerDiv, and cause complaints.
+        }
+        popup.map = null;
+    },
+
+  /********************************************************/
+  /*                                                      */
+  /*              Container Div Functions                 */
+  /*                                                      */
+  /*   The following functions deal with the access to    */
+  /*    and maintenance of the size of the container div  */
+  /*                                                      */
+  /********************************************************/     
+
+    /**
+    * @returns An OpenLayers.Size object that represents the size, in pixels, 
+    *          of the div into which OpenLayers has been loaded. 
+    * 
+    *          Note: A clone() of this locally cached variable is returned, so 
+    *                as not to allow users to modify it.
+    * 
+    * @type OpenLayers.Size
+    */
+    getSize: function () {
+        var size = null;
+        if (this.size != null) {
+            size = this.size.clone();
+        }
+        return size;
+    },
+
+    /**
+    * This function should be called by any external code which dynamically
+    * changes the size of the map div (because mozilla wont let us catch the
+    * "onresize" for an element)
+    */
+    updateSize: function() {
+        var newSize = this.getCurrentSize();
+        var oldSize = this.getSize();
+        if (oldSize == null)
+            this.size = oldSize = newSize;
+        if (!newSize.equals(oldSize)) {
+            
+            //notify layers of mapresize
+            for(var i=0; i < this.layers.length; i++) {
+                this.layers[i].onMapResize();                
+            }
+            
+            // store the new size
+            this.size = newSize;
+            // the div might have moved on the page, also
+            this.events.element.offsets = null;
+
+            if (this.baseLayer != null) {
+                var center = new OpenLayers.Pixel(newSize.w /2, newSize.h / 2);
+                var centerLL = this.getLonLatFromViewPortPx(center);
+                var zoom = this.getZoom();
+                this.zoom = null;
+                this.setCenter(this.getCenter(), zoom);
+            }
+
+        }
+    },
+    
+    /**
+     * @private 
+     * 
+     * @returns A new OpenLayers.Size object with the dimensions of the map div
+     * @type OpenLayers.Size
+     */
+    getCurrentSize: function() {
+
+        var size = new OpenLayers.Size(this.div.clientWidth, 
+                                       this.div.clientHeight);
+
+        // Workaround for the fact that hidden elements return 0 for size.
+        if (size.w == 0 && size.h == 0) {
+            var dim = OpenLayers.Element.getDimensions(this.div);
+            size.w = dim.width;
+            size.h = dim.height;
+        }
+        if (size.w == 0 && size.h == 0) {
+            size.w = parseInt(this.div.style.width);
+            size.h = parseInt(this.div.style.height);
+        }
+        return size;
+    },
+
+    /** 
+     * @param {OpenLayers.LonLat} center Default is this.getCenter()
+     * @param {float} resolution Default is this.getResolution() 
+     * 
+     * @returns A Bounds based on resolution, center, and current mapsize.
+     * @type OpenLayers.Bounds
+     */
+    calculateBounds: function(center, resolution) {
+
+        var extent = null;
+        
+        if (center == null) {
+            center = this.getCenter();
+        }                
+        if (resolution == null) {
+            resolution = this.getResolution();
+        }
+    
+        if ((center != null) && (resolution != null)) {
+
+            var size = this.getSize();
+            var w_deg = size.w * resolution;
+            var h_deg = size.h * resolution;
+        
+            extent = new OpenLayers.Bounds(center.lon - w_deg / 2,
+                                           center.lat - h_deg / 2,
+                                           center.lon + w_deg / 2,
+                                           center.lat + h_deg / 2);
+        
+        }
+
+        return extent;
+    },
+
+
+  /********************************************************/
+  /*                                                      */
+  /*            Zoom, Center, Pan Functions               */
+  /*                                                      */
+  /*    The following functions handle the validation,    */
+  /*   getting and setting of the Zoom Level and Center   */
+  /*       as well as the panning of the Map              */
+  /*                                                      */
+  /********************************************************/
+    /**
+    * @return {OpenLayers.LonLat}
+    */
+    getCenter: function () {
+        return this.center;
+    },
+
+
+    /**
+    * @return {int}
+    */
+    getZoom: function () {
+        return this.zoom;
+    },
+    
+    /** Allows user to pan by a value of screen pixels
+     * 
+     * @param {int} dx
+     * @param {int} dy
+     */
+    pan: function(dx, dy) {
+
+        // getCenter
+        var centerPx = this.getViewPortPxFromLonLat(this.getCenter());
+
+        // adjust
+        var newCenterPx = centerPx.add(dx, dy);
+        
+        // only call setCenter if there has been a change
+        if (!newCenterPx.equals(centerPx)) {
+            var newCenterLonLat = this.getLonLatFromViewPortPx(newCenterPx);
+            this.setCenter(newCenterLonLat);
+        }
+
+   },
+
+    /**
+    * @param {OpenLayers.LonLat} lonlat
+    * @param {int} zoom
+    * @param {Boolean} dragging Specifies whether or not to 
+    *                           trigger movestart/end events
+    */
+    setCenter: function (lonlat, zoom, dragging) {
+        
+        if (!this.center && !this.isValidLonLat(lonlat)) {
+            lonlat = this.maxExtent.getCenterLonLat();
+        }
+        
+        var zoomChanged = (this.isValidZoomLevel(zoom)) && 
+                          (zoom != this.getZoom());
+
+        var centerChanged = (this.isValidLonLat(lonlat)) && 
+                            (!lonlat.equals(this.center));
+
+
+        // if neither center nor zoom will change, no need to do anything
+        if (zoomChanged || centerChanged || !dragging) {
+
+            if (!dragging) { this.events.triggerEvent("movestart"); }
+
+            if (centerChanged) {
+                if ((!zoomChanged) && (this.center)) { 
+                    // if zoom hasnt changed, just slide layerContainer
+                    //  (must be done before setting this.center to new value)
+                    this.centerLayerContainer(lonlat);
+                }
+                this.center = lonlat.clone();
+            }
+
+            // (re)set the layerContainerDiv's location
+            if ((zoomChanged) || (this.layerContainerOrigin == null)) {
+                this.layerContainerOrigin = this.center.clone();
+                this.layerContainerDiv.style.left = "0px";
+                this.layerContainerDiv.style.top  = "0px";
+            }
+
+            if (zoomChanged) {
+                this.zoom = zoom;
+                    
+                //redraw popups
+                for (var i = 0; i < this.popups.length; i++) {
+                    this.popups[i].updatePosition();
+                }
+            }    
+            
+            var bounds = this.getExtent();
+            
+            //send the move call to the baselayer and all the overlays    
+            this.baseLayer.moveTo(bounds, zoomChanged, dragging);
+            for (var i = 0; i < this.layers.length; i++) {
+                var layer = this.layers[i];
+                if (!layer.isBaseLayer) {
+                    
+                    var moveLayer;
+                    var inRange = layer.calculateInRange();
+                    if (layer.inRange != inRange) {
+                        // Layer property has changed. We are going 
+                        // to call moveLayer so that the layer can be turned
+                        // off or on.   
+                        layer.inRange = inRange;
+                        moveLayer = true;
+                        this.events.triggerEvent("changelayer");
+                    } else {
+                        // If nothing has changed, then we only move the layer
+                        // if it is visible and inrange.
+                        moveLayer = (layer.visibility && layer.inRange);
+                    }
+
+                    if (moveLayer) {
+                        layer.moveTo(bounds, zoomChanged, dragging);
+                    }
+                }                
+            }
+            
+            this.events.triggerEvent("move");
+    
+            if (zoomChanged) { this.events.triggerEvent("zoomend"); }
+        }
+
+        // even if nothing was done, we want to notify of this
+        if (!dragging) { this.events.triggerEvent("moveend"); }
+    },
+
+    /** This function takes care to recenter the layerContainerDiv 
+     * 
+     * @private 
+     * 
+     * @param {OpenLayers.LonLat} lonlat
+     */
+    centerLayerContainer: function (lonlat) {
+
+        var originPx = this.getViewPortPxFromLonLat(this.layerContainerOrigin);
+        var newPx = this.getViewPortPxFromLonLat(lonlat);
+
+        if ((originPx != null) && (newPx != null)) {
+            this.layerContainerDiv.style.left = (originPx.x - newPx.x) + "px";
+            this.layerContainerDiv.style.top  = (originPx.y - newPx.y) + "px";
+        }
+    },
+
+    /**
+     * @private 
+     * 
+     * @param {int} zoomLevel
+     * 
+     * @returns Whether or not the zoom level passed in is non-null and 
+     *           within the min/max range of zoom levels.
+     * @type Boolean
+     */
+    isValidZoomLevel: function(zoomLevel) {
+       return ( (zoomLevel != null) &&
+                (zoomLevel >= 0) && 
+                (zoomLevel < this.getNumZoomLevels()) );
+    },
+    
+    /**
+     * @private 
+     * 
+     * @param {OpenLayers.LonLat} lonlat
+     * 
+     * @returns Whether or not the lonlat passed in is non-null and within
+     *             the maxExtent bounds
+     * 
+     * @type Boolean
+     */
+    isValidLonLat: function(lonlat) {
+        var valid = false;
+        if (lonlat != null) {
+            var maxExtent = this.getMaxExtent();
+            valid = maxExtent.containsLonLat(lonlat);        
+        }
+        return valid;
+    },
+
+  /********************************************************/
+  /*                                                      */
+  /*                 Layer Options                        */
+  /*                                                      */
+  /*    Accessor functions to Layer Options parameters    */
+  /*                                                      */
+  /********************************************************/
+    
+    /**
+     * @returns The Projection of the base layer
+     * @type String
+     */
+    getProjection: function() {
+        var projection = null;
+        if (this.baseLayer != null) {
+            projection = this.baseLayer.projection;
+        }
+        return projection;
+    },
+    
+    /**
+     * @returns The Map's Maximum Resolution
+     * @type String
+     */
+    getMaxResolution: function() {
+        var maxResolution = null;
+        if (this.baseLayer != null) {
+            maxResolution = this.baseLayer.maxResolution;
+        }
+        return maxResolution;
+    },
+        
+    /**
+    * @type OpenLayers.Bounds
+    */
+    getMaxExtent: function () {
+        var maxExtent = null;
+        if (this.baseLayer != null) {
+            maxExtent = this.baseLayer.maxExtent;
+        }        
+        return maxExtent;
+    },
+    
+    /**
+     * @returns The total number of zoom levels that can be displayed by the 
+     *           current baseLayer.
+     * @type int
+     */
+    getNumZoomLevels: function() {
+        var numZoomLevels = null;
+        if (this.baseLayer != null) {
+            numZoomLevels = this.baseLayer.numZoomLevels;
+        }
+        return numZoomLevels;
+    },
+
+  /********************************************************/
+  /*                                                      */
+  /*                 Baselayer Functions                  */
+  /*                                                      */
+  /*    The following functions, all publicly exposed     */
+  /*       in the API?, are all merely wrappers to the    */
+  /*       the same calls on whatever layer is set as     */
+  /*                the current base layer                */
+  /*                                                      */
+  /********************************************************/
+
+    /**
+     * @returns A Bounds object which represents the lon/lat bounds of the 
+     *          current viewPort. 
+     *          If no baselayer is set, returns null.
+     * @type OpenLayers.Bounds
+     */
+    getExtent: function () {
+        var extent = null;
+        if (this.baseLayer != null) {
+            extent = this.baseLayer.getExtent();
+        }
+        return extent;
+    },
+
+    /**
+     * @returns The current resolution of the map. 
+     *          If no baselayer is set, returns null.
+     * @type float
+     */
+    getResolution: function () {
+        var resolution = null;
+        if (this.baseLayer != null) {
+            resolution = this.baseLayer.getResolution();
+        }
+        return resolution;
+    },
+
+     /**
+      * @returns The current scale denominator of the map. 
+      *          If no baselayer is set, returns null.
+      * @type float
+      */
+    getScale: function () {
+        var scale = null;
+        if (this.baseLayer != null) {
+            var res = this.getResolution();
+            var units = this.baseLayer.units;
+            scale = OpenLayers.Util.getScaleFromResolution(res, units);
+        }
+        return scale;
+    },
+
+
+    /**
+     * @param {OpenLayers.Bounds} bounds
+     *
+     * @returns A suitable zoom level for the specified bounds.
+     *          If no baselayer is set, returns null.
+     * @type int
+     */
+    getZoomForExtent: function (bounds) {
+        var zoom = null;
+        if (this.baseLayer != null) {
+            zoom = this.baseLayer.getZoomForExtent(bounds);
+        }
+        return zoom;
+    },
+
+    /**
+     * @param {float} resolution
+     *
+     * @returns A suitable zoom level for the specified resolution.
+     *          If no baselayer is set, returns null.
+     * @type int
+     */
+    getZoomForResolution: function(resolution) {
+        var zoom = null;
+        if (this.baseLayer != null) {
+            zoom = this.baseLayer.getZoomForResolution(resolution);
+        }
+        return zoom;
+    },
+
+  /********************************************************/
+  /*                                                      */
+  /*                  Zooming Functions                   */
+  /*                                                      */
+  /*    The following functions, all publicly exposed     */
+  /*       in the API, are all merely wrappers to the     */
+  /*               the setCenter() function               */
+  /*                                                      */
+  /********************************************************/
+  
+    /** Zoom to a specific zoom level
+     * 
+     * @param {int} zoom
+     */
+    zoomTo: function(zoom) {
+        if (this.isValidZoomLevel(zoom)) {
+            this.setCenter(null, zoom);
+        }
+    },
+    
+    /**
+     * @param {int} zoom
+     */
+    zoomIn: function() {
+        this.zoomTo(this.getZoom() + 1);
+    },
+    
+    /**
+     * @param {int} zoom
+     */
+    zoomOut: function() {
+        this.zoomTo(this.getZoom() - 1);
+    },
+
+    /** Zoom to the passed in bounds, recenter
+     * 
+     * @param {OpenLayers.Bounds} bounds
+     */
+    zoomToExtent: function(bounds) {
+        this.setCenter(bounds.getCenterLonLat(), 
+                       this.getZoomForExtent(bounds));
+    },
+
+    /** Zoom to the full extent and recenter.
+     */
+    zoomToMaxExtent: function() {
+        this.zoomToExtent(this.getMaxExtent());
+    },
+
+    /** zoom to a specified scale 
+     * 
+     * @param {float} scale
+     */
+    zoomToScale: function(scale) {
+        var res = OpenLayers.Util.getResolutionFromScale(scale, 
+                                                         this.baseLayer.units);
+        var size = this.getSize();
+        var w_deg = size.w * res;
+        var h_deg = size.h * res;
+        var center = this.getCenter();
+
+        var extent = new OpenLayers.Bounds(center.lon - w_deg / 2,
+                                           center.lat - h_deg / 2,
+                                           center.lon + w_deg / 2,
+                                           center.lat + h_deg / 2);
+        this.zoomToExtent(extent);
+    },
+    
+  /********************************************************/
+  /*                                                      */
+  /*             Translation Functions                    */
+  /*                                                      */
+  /*      The following functions translate between       */
+  /*           LonLat, LayerPx, and ViewPortPx            */
+  /*                                                      */
+  /********************************************************/
+      
+  //
+  // TRANSLATION: LonLat <-> ViewPortPx
+  //
+
+    /**
+    * @param {OpenLayers.Pixel} viewPortPx
+    *
+    * @returns An OpenLayers.LonLat which is the passed-in view port
+    *          OpenLayers.Pixel, translated into lon/lat by the 
+    *          current base layer
+    * @type OpenLayers.LonLat
+    * @private
+    */
+    getLonLatFromViewPortPx: function (viewPortPx) {
+        var lonlat = null; 
+        if (this.baseLayer != null) {
+            lonlat = this.baseLayer.getLonLatFromViewPortPx(viewPortPx);
+        }
+        return lonlat;
+    },
+
+    /**
+    * @param {OpenLayers.LonLat} lonlat
+    *
+    * @returns An OpenLayers.Pixel which is the passed-in OpenLayers.LonLat, 
+    *          translated into view port pixels by the 
+    *          current base layer
+    * @type OpenLayers.Pixel
+    * @private
+    */
+    getViewPortPxFromLonLat: function (lonlat) {
+        var px = null; 
+        if (this.baseLayer != null) {
+            px = this.baseLayer.getViewPortPxFromLonLat(lonlat);
+        }
+        return px;
+    },
+
+    
+  //
+  // CONVENIENCE TRANSLATION FUNCTIONS FOR API
+  //
+
+    /**
+     * @param {OpenLayers.Pixel} pixel
+     *
+     * @returns An OpenLayers.LonLat corresponding to the given
+     *          OpenLayers.Pixel, translated into lon/lat by the 
+     *          current base layer
+     * @type OpenLayers.LonLat
+     */
+    getLonLatFromPixel: function (px) {
+        return this.getLonLatFromViewPortPx(px);
+    },
+
+    /**
+     * @param {OpenLayers.LonLat} lonlat
+     *
+     * @returns An OpenLayers.Pixel corresponding to the OpenLayers.LonLat
+     *          translated into view port pixels by the 
+     *          current base layer
+     * @type OpenLayers.Pixel
+     */
+    getPixelFromLonLat: function (lonlat) {
+        return this.getViewPortPxFromLonLat(lonlat);
+    },
+
+
+
+  //
+  // TRANSLATION: ViewPortPx <-> LayerPx
+  //
+
+    /**
+     * @private
+     * 
+     * @param {OpenLayers.Pixel} layerPx
+     * 
+     * @returns Layer Pixel translated into ViewPort Pixel coordinates
+     * @type OpenLayers.Pixel
+     */
+    getViewPortPxFromLayerPx:function(layerPx) {
+        var viewPortPx = null;
+        if (layerPx != null) {
+            var dX = parseInt(this.layerContainerDiv.style.left);
+            var dY = parseInt(this.layerContainerDiv.style.top);
+            viewPortPx = layerPx.add(dX, dY);            
+        }
+        return viewPortPx;
+    },
+    
+    /**
+     * @private
+     * 
+     * @param {OpenLayers.Pixel} viewPortPx
+     * 
+     * @returns ViewPort Pixel translated into Layer Pixel coordinates
+     * @type OpenLayers.Pixel
+     */
+    getLayerPxFromViewPortPx:function(viewPortPx) {
+        var layerPx = null;
+        if (viewPortPx != null) {
+            var dX = -parseInt(this.layerContainerDiv.style.left);
+            var dY = -parseInt(this.layerContainerDiv.style.top);
+            layerPx = viewPortPx.add(dX, dY);
+            if (isNaN(layerPx.x) || isNaN(layerPx.y)) {
+                layerPx = null;
+            }
+        }
+        return layerPx;
+    },
+    
+  //
+  // TRANSLATION: LonLat <-> LayerPx
+  //
+
+    /**
+    * @param {OpenLayers.Pixel} px
+    *
+    * @type OpenLayers.LonLat
+    */
+    getLonLatFromLayerPx: function (px) {
+       //adjust for displacement of layerContainerDiv
+       px = this.getViewPortPxFromLayerPx(px);
+       return this.getLonLatFromViewPortPx(px);         
+    },
+    
+    /**
+    * @param {OpenLayers.LonLat} lonlat
+    *
+    * @returns An OpenLayers.Pixel which is the passed-in OpenLayers.LonLat, 
+    *          translated into layer pixels by the current base layer
+    * @type OpenLayers.Pixel
+    */
+    getLayerPxFromLonLat: function (lonlat) {
+       //adjust for displacement of layerContainerDiv
+       var px = this.getViewPortPxFromLonLat(lonlat);
+       return this.getLayerPxFromViewPortPx(px);         
+    },
+
+
+    CLASS_NAME: "OpenLayers.Map"
+};


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Map.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Marker/Box.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Marker/Box.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Marker/Box.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,74 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
+ * for the full text of the license. */
+
+
+/**
+ * @class
+ * 
+ * @requires OpenLayers/Marker.js
+ */
+OpenLayers.Marker.Box = OpenLayers.Class.create();
+OpenLayers.Marker.Box.prototype = OpenLayers.Class.inherit( OpenLayers.Marker, {
+    /** @type OpenLayers.LonLat */
+    bounds: null,
+
+    div: null,
+    
+    /** 
+    * @constructor
+    *
+    * @param {OpenLayers.Icon} icon
+    * @param {OpenLayers.LonLat lonlat
+    */
+    initialize: function(bounds, borderColor, borderWidth) {
+        this.bounds = bounds;
+        this.div    = OpenLayers.Util.createDiv();
+        this.div.style.overflow = 'hidden';
+        this.events = new OpenLayers.Events(this, this.div, null);
+        this.setBorder(borderColor, borderWidth);
+    },
+
+    setBorder: function (color, width) {
+        if (!color) color = "red";
+        if (!width) width = 2;
+        this.div.style.border = width + "px solid " + color;
+    },
+    
+    /** 
+    * @param {OpenLayers.Pixel} px
+    * 
+    * @return A new DOM Image with this marker´s icon set at the 
+    *         location passed-in
+    * @type DOMElement
+    */
+    draw: function(px, sz) {
+        OpenLayers.Util.modifyDOMElement(this.div, null, px, sz);
+        return this.div;
+    }, 
+
+    /**
+     * @returns Whether or not the marker is currently visible on screen.
+     * @type Boolean
+     */
+    onScreen:function() {
+        var onScreen = false;
+        if (this.map) {
+            var screenBounds = this.map.getExtent();
+            onScreen = screenBounds.containsBounds(this.bounds, true, true);
+        }    
+        return onScreen;
+    },
+    
+    /** Hide or show the icon
+     * 
+     * @param {Boolean} display
+     */
+    display: function(display) {
+        this.div.style.display = (display) ? "" : "none";
+    },
+    
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.Marker.Box"
+});
+


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Marker/Box.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Marker.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Marker.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Marker.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,132 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
+ * for the full text of the license. */
+
+
+/**
+ * @class
+ */
+OpenLayers.Marker = OpenLayers.Class.create();
+OpenLayers.Marker.prototype = {
+    
+    /** @type OpenLayers.Icon */
+    icon: null,
+
+    /** location of object
+    * @type OpenLayers.LonLat */
+    lonlat: null,
+    
+    /** @type OpenLayers.Events*/
+    events: null,
+    
+    /** @type OpenLayers.Map */
+    map: null,
+    
+    /** 
+    * @constructor
+    *
+    * @param {OpenLayers.Icon} icon
+    * @param {OpenLayers.LonLat lonlat
+    */
+    initialize: function(lonlat, icon) {
+        this.lonlat = lonlat;
+        
+        var newIcon = (icon) ? icon : OpenLayers.Marker.defaultIcon();
+        if (this.icon == null) {
+            this.icon = newIcon;
+        } else {
+            this.icon.url = newIcon.url;
+            this.icon.size = newIcon.size;
+            this.icon.offset = newIcon.offset;
+            this.icon.calculateOffset = newIcon.calculateOffset;
+        }
+        this.events = new OpenLayers.Events(this, this.icon.imageDiv, null);
+    },
+    
+    destroy: function() {
+        this.map = null;
+        
+        if (this.icon != null) {
+            this.icon.destroy();
+            this.icon = null;
+        }
+    },
+    
+    /** 
+    * @param {OpenLayers.Pixel} px
+    * 
+    * @return A new DOM Image with this marker´s icon set at the 
+    *         location passed-in
+    * @type DOMElement
+    */
+    draw: function(px) {
+        return this.icon.draw(px);
+    }, 
+
+    /**
+    * @param {OpenLayers.Pixel} px
+    */
+    moveTo: function (px) {
+        if ((px != null) && (this.icon != null)) {
+            this.icon.moveTo(px);
+        }            
+    },
+
+    /**
+     * @returns Whether or not the marker is currently visible on screen.
+     * @type Boolean
+     */
+    onScreen:function() {
+        
+        var onScreen = false;
+        if (this.map) {
+            var screenBounds = this.map.getExtent();
+            onScreen = screenBounds.containsLonLat(this.lonlat);
+        }    
+        return onScreen;
+    },
+    
+    /**
+     * @param {float} inflate
+     */
+    inflate: function(inflate) {
+        if (this.icon) {
+            var newSize = new OpenLayers.Size(this.icon.size.w * inflate,
+                                              this.icon.size.h * inflate);
+            this.icon.setSize(newSize);
+        }        
+    },
+    
+    /** Change the opacity of the marker by changin the opacity of 
+     *   its icon
+     * 
+     * @param {float} opacity Specified as fraction (0.4, etc)
+     */
+    setOpacity: function(opacity) {
+        this.icon.setOpacity(opacity);
+    },
+
+    /** Hide or show the icon
+     * 
+     * @param {Boolean} display
+     */
+    display: function(display) {
+        this.icon.display(display);
+    },
+    
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.Marker"
+};
+
+
+/** 
+ * @returns A default OpenLayers.Icon to use for a marker
+ * @type OpenLayers.Icon
+ */
+OpenLayers.Marker.defaultIcon = function() {
+    var url = OpenLayers.Util.getImagesLocation() + "marker.png";
+    var size = new OpenLayers.Size(21, 25);
+    return new OpenLayers.Icon(url, size);        
+};
+    
+


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Marker.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Popup/Anchored.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Popup/Anchored.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Popup/Anchored.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,128 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
+ * for the full text of the license. */
+
+
+/**
+ * @class
+ * 
+ * @requires OpenLayers/Popup.js
+ */
+OpenLayers.Popup.Anchored = OpenLayers.Class.create();
+OpenLayers.Popup.Anchored.prototype =
+   OpenLayers.Class.inherit( OpenLayers.Popup, {
+
+    /** "lr", "ll", "tr", "tl" - relative position of the popup.
+     * @type String */
+    relativePosition: null,
+
+    /** Object which must have expose a 'size' (OpenLayers.Size) and 
+     *                                 'offset' (OpenLayers.Pixel) 
+     * @type Object */
+    anchor: null,
+
+    /** 
+    * @constructor
+    * 
+    * @param {String} id
+    * @param {OpenLayers.LonLat} lonlat
+    * @param {OpenLayers.Size} size
+    * @param {String} contentHTML
+    * @param {Object} anchor  Object which must expose a 
+    *                         - 'size' (OpenLayers.Size) and 
+    *                         - 'offset' (OpenLayers.Pixel) 
+    *                         (this is generally an OpenLayers.Icon)
+    * @param {Boolean} closeBox
+    */
+    initialize:function(id, lonlat, size, contentHTML, anchor, closeBox) {
+        var newArguments = new Array(id, lonlat, size, contentHTML, closeBox);
+        OpenLayers.Popup.prototype.initialize.apply(this, newArguments);
+
+        this.anchor = (anchor != null) ? anchor 
+                                       : { size: new OpenLayers.Size(0,0),
+                                           offset: new OpenLayers.Pixel(0,0)};
+    },
+
+    /** 
+    * @param {OpenLayers.Pixel} px
+    * 
+    * @returns Reference to a div that contains the drawn popup
+    * @type DOMElement
+    */
+    draw: function(px) {
+        if (px == null) {
+            if ((this.lonlat != null) && (this.map != null)) {
+                px = this.map.getLayerPxFromLonLat(this.lonlat);
+            }
+        }
+        
+        //calculate relative position
+        this.relativePosition = this.calculateRelativePosition(px);
+        
+        return OpenLayers.Popup.prototype.draw.apply(this, arguments);
+    },
+    
+    /** 
+     * @private
+     * 
+     * @param {OpenLayers.Pixel} px
+     * 
+     * @returns The relative position ("br" "tr" "tl "bl") at which the popup
+     *           should be placed
+     * @type String
+     */
+    calculateRelativePosition:function(px) {
+        var lonlat = this.map.getLonLatFromLayerPx(px);        
+        
+        var extent = this.map.getExtent();
+        var quadrant = extent.determineQuadrant(lonlat);
+        
+        return OpenLayers.Bounds.oppositeQuadrant(quadrant);
+    }, 
+
+    /**
+    * @param {OpenLayers.Pixel} px
+    */
+    moveTo: function(px) {
+        
+        var newPx = this.calculateNewPx(px);
+        
+        var newArguments = new Array(newPx);        
+        OpenLayers.Popup.prototype.moveTo.apply(this, newArguments);
+    },
+    
+    /**
+    * @param {OpenLayers.Size} size
+    */
+    setSize:function(size) { 
+        OpenLayers.Popup.prototype.setSize.apply(this, arguments);
+
+        if ((this.lonlat) && (this.map)) {
+            var px = this.map.getLayerPxFromLonLat(this.lonlat);
+            this.moveTo(px);
+        }
+    },  
+    
+    /** 
+     * @private 
+     * 
+     * @param {OpenLayers.Pixel} px
+     * 
+     * @returns The the new px position of the popup on the screen
+     *           relative to the passed-in px
+     * @type OpenLayers.Pixel
+     */
+    calculateNewPx:function(px) {
+        var newPx = px.offset(this.anchor.offset);
+
+        var top = (this.relativePosition.charAt(0) == 't');
+        newPx.y += (top) ? -this.size.h : this.anchor.size.h;
+        
+        var left = (this.relativePosition.charAt(1) == 'l');
+        newPx.x += (left) ? -this.size.w : this.anchor.size.w;
+
+        return newPx;   
+    },
+
+    CLASS_NAME: "OpenLayers.Popup.Anchored"
+});


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Popup/Anchored.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Popup/AnchoredBubble.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Popup/AnchoredBubble.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Popup/AnchoredBubble.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,165 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
+ * for the full text of the license. */
+
+
+/**
+ * @class
+ * 
+ * @requires OpenLayers/Popup/Anchored.js
+ */
+OpenLayers.Popup.AnchoredBubble = OpenLayers.Class.create();
+
+//Border space for the rico corners
+OpenLayers.Popup.AnchoredBubble.CORNER_SIZE = 5;
+
+OpenLayers.Popup.AnchoredBubble.prototype =
+   OpenLayers.Class.inherit( OpenLayers.Popup.Anchored, {
+
+    rounded: false, 
+    
+    /** 
+    * @constructor
+    * 
+    * @param {String} id
+    * @param {OpenLayers.LonLat} lonlat
+    * @param {OpenLayers.Size} size
+    * @param {String} contentHTML
+    * @param {Object} anchor  Object which must expose a 
+    *                         - 'size' (OpenLayers.Size) and 
+    *                         - 'offset' (OpenLayers.Pixel) 
+    *                         (this is generally an OpenLayers.Icon)
+    * @param {Boolean} closeBox
+    */
+    initialize:function(id, lonlat, size, contentHTML, anchor, closeBox) {
+        OpenLayers.Popup.Anchored.prototype.initialize.apply(this, arguments);
+    },
+
+    /** 
+    * @param {OpenLayers.Pixel} px
+    * 
+    * @returns Reference to a div that contains the drawn popup
+    * @type DOMElement
+    */
+    draw: function(px) {
+        
+        OpenLayers.Popup.Anchored.prototype.draw.apply(this, arguments);
+
+        this.setContentHTML();
+        
+        this.setRicoCorners(!this.rounded);
+        this.rounded = true;
+        
+        //set the popup color and opacity           
+        this.setBackgroundColor(); 
+        this.setOpacity();
+
+        return this.div;
+    },
+
+    /**
+    * @param {OpenLayers.Size} size
+    */
+    setSize:function(size) { 
+        OpenLayers.Popup.Anchored.prototype.setSize.apply(this, arguments);
+        
+        if (this.contentDiv != null) {
+
+            var contentSize = this.size.clone();
+            contentSize.h -= (2 * OpenLayers.Popup.AnchoredBubble.CORNER_SIZE);
+            contentSize.h -= (2 * this.padding);
+    
+            this.contentDiv.style.height = contentSize.h + "px";
+            
+            //size has changed - must redo corners        
+            this.setRicoCorners(!this.rounded);
+            this.rounded = true;
+        }
+    },  
+
+    /**
+     * @param {String} color
+     */
+    setBackgroundColor:function(color) { 
+        if (color != undefined) {
+            this.backgroundColor = color; 
+        }
+        
+        if (this.div != null) {
+            if (this.contentDiv != null) {
+                this.div.style.background = "transparent";
+                Rico.Corner.changeColor(this.contentDiv, this.backgroundColor);
+            }
+        }
+    },  
+    
+    /**
+     * @param {float} opacity
+     */
+    setOpacity:function(opacity) { 
+        if (opacity != undefined) {
+            this.opacity = opacity; 
+        }
+        
+        if (this.div != null) {
+            if (this.contentDiv != null) {
+            Rico.Corner.changeOpacity(this.contentDiv, this.opacity);
+            }
+        }
+    },  
+ 
+    /** Bubble Popups can not have a border
+     * 
+     * @param {int} border
+     */
+    setBorder:function(border) { 
+        this.border = 0;
+    },      
+ 
+    /** 
+     * @private
+     * 
+     * @param {Boolean} firstTime Is this the first time the corners are being
+     *                             rounded?
+     * 
+     * update the rico corners according to the popup's
+     * current relative postion 
+     */
+    setRicoCorners:function(firstTime) {
+    
+        var corners = this.getCornersToRound(this.relativePosition);
+        var options = {corners: corners,
+                         color: this.backgroundColor,
+                       bgColor: "transparent",
+                         blend: false};
+
+        if (firstTime) {
+            Rico.Corner.round(this.div, options);
+        } else {
+            Rico.Corner.reRound(this.contentDiv, options);
+            //set the popup color and opacity
+            this.setBackgroundColor(); 
+            this.setOpacity();
+        }
+    },
+
+    /** 
+     * @private
+     * 
+     * @returns The proper corners string ("tr tl bl br") for rico
+     *           to round
+     * @type String
+     */
+    getCornersToRound:function() {
+
+        var corners = ['tl', 'tr', 'bl', 'br'];
+
+        //we want to round all the corners _except_ the opposite one. 
+        var corner = OpenLayers.Bounds.oppositeQuadrant(this.relativePosition);
+        OpenLayers.Util.removeItem(corners, corner);
+
+        return corners.join(" ");
+    },
+
+    CLASS_NAME: "OpenLayers.Popup.AnchoredBubble"
+});


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Popup/AnchoredBubble.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Popup.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Popup.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Popup.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,349 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
+ * for the full text of the license. */
+
+
+/**
+ * @class
+ */
+OpenLayers.Popup = OpenLayers.Class.create();
+
+OpenLayers.Popup.WIDTH = 200;
+OpenLayers.Popup.HEIGHT = 200;
+OpenLayers.Popup.COLOR = "white";
+OpenLayers.Popup.OPACITY = 1;
+OpenLayers.Popup.BORDER = "0px";
+
+OpenLayers.Popup.prototype = {
+
+    /** @type OpenLayers.Events*/
+    events: null,
+    
+    /** @type String */
+    id: "",
+
+    /** @type OpenLayers.LonLat */
+    lonlat: null,
+
+    /** @type DOMElement */
+    div: null,
+
+    /** @type OpenLayers.Size*/
+    size: null,    
+
+    /** @type String */
+    contentHTML: "",
+    
+    /** @type String */
+    backgroundColor: "",
+    
+    /** @type float */
+    opacity: "",
+
+    /** @type String */
+    border: "",
+    
+    /** @type DOMElement */
+    contentDiv:null,
+
+    /** @type int */
+    padding: 5,
+
+
+    /** this gets set in Map.js when the popup is added to the map
+     * @type OpenLayers.Map */
+    map: null,
+
+    /** 
+    * @constructor
+    * 
+    * @param {String} id
+    * @param {OpenLayers.LonLat} lonlat
+    * @param {OpenLayers.Size} size
+    * @param {String} contentHTML
+    * @param {Boolean} closeBox
+    */
+    initialize:function(id, lonlat, size, contentHTML, closeBox) {
+        if (id == null) {
+            id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
+        }
+
+        this.id = id;
+        this.lonlat = lonlat;
+        this.size = (size != null) ? size 
+                                  : new OpenLayers.Size(
+                                                   OpenLayers.Popup.WIDTH,
+                                                   OpenLayers.Popup.HEIGHT);
+        if (contentHTML != null) { 
+             this.contentHTML = contentHTML;
+        }
+        this.backgroundColor = OpenLayers.Popup.COLOR;
+        this.opacity = OpenLayers.Popup.OPACITY;
+        this.border = OpenLayers.Popup.BORDER;
+
+        this.div = OpenLayers.Util.createDiv(this.id, null, null, 
+                                             null, null, null, "hidden");
+        this.div.className = 'olPopup';
+
+        var id = this.div.id + "_contentDiv";
+        this.contentDiv = OpenLayers.Util.createDiv(id, null, this.size.clone(), 
+                                                    null, "relative", null,
+                                                    "hidden");
+        this.contentDiv.className = 'olPopupContent';                                            
+        this.div.appendChild(this.contentDiv);
+
+        if (closeBox == true) {
+           // close icon
+            var closeSize = new OpenLayers.Size(17,17);
+            var img = OpenLayers.Util.getImagesLocation() + "close.gif";
+            var closeImg = OpenLayers.Util.createAlphaImageDiv(this.id + "_close", 
+                                                                null, 
+                                                                closeSize, 
+                                                                img);
+            closeImg.style.right = this.padding + "px";
+            closeImg.style.top = this.padding + "px";
+            this.div.appendChild(closeImg);
+
+            var closeEvents = new OpenLayers.Events(this, closeImg);
+            closeEvents.register("mousedown", this, this.hide);
+
+        }
+
+        this.registerEvents();
+    },
+
+    /** 
+    */
+    destroy: function() {
+        if (this.map != null) {
+            this.map.removePopup(this);
+        }
+        this.div = null;
+        this.map = null;
+    },
+
+    /** 
+    * @param {OpenLayers.Pixel} px
+    * 
+    * @returns Reference to a div that contains the drawn popup
+    * @type DOMElement
+    */
+    draw: function(px) {
+        if (px == null) {
+            if ((this.lonlat != null) && (this.map != null)) {
+                px = this.map.getLayerPxFromLonLat(this.lonlat);
+            }
+        }
+        
+        this.setSize();
+        this.setBackgroundColor();
+        this.setOpacity();
+        this.setBorder();
+        this.setContentHTML();
+        this.moveTo(px);
+
+        return this.div;
+    },
+
+    /** 
+     * if the popup has a lonlat and its map members set, 
+     *  then have it move itself to its proper position
+     */
+    updatePosition: function() {
+        if ((this.lonlat) && (this.map)) {
+                var px = this.map.getLayerPxFromLonLat(this.lonlat);
+                this.moveTo(px);            
+        }
+    },
+
+    /**
+    * @param {OpenLayers.Pixel} px
+    */
+    moveTo: function(px) {
+        if ((px != null) && (this.div != null)) {
+            this.div.style.left = px.x + "px";
+            this.div.style.top = px.y + "px";
+        }
+    },
+
+    /**
+     * @returns Boolean indicating whether or not the popup is visible
+     * @type Boolean
+     */
+    visible: function() {
+        return OpenLayers.Element.visible(this.div);
+    },
+
+    /**
+     * 
+     */
+    toggle: function() {
+        OpenLayers.Element.toggle(this.div);
+    },
+
+    /**
+     *
+     */
+    show: function() {
+        OpenLayers.Element.show(this.div);
+    },
+
+    /**
+     *
+     */
+    hide: function() {
+        OpenLayers.Element.hide(this.div);
+    },
+
+    /**
+    * @param {OpenLayers.Size} size
+    */
+    setSize:function(size) { 
+        if (size != undefined) {
+            this.size = size; 
+        }
+        
+        if (this.div != null) {
+            this.div.style.width = this.size.w + "px";
+            this.div.style.height = this.size.h + "px";
+        }
+    },  
+
+    /**
+    * @param {String} color
+    */
+    setBackgroundColor:function(color) { 
+        if (color != undefined) {
+            this.backgroundColor = color; 
+        }
+        
+        if (this.div != null) {
+            this.div.style.backgroundColor = this.backgroundColor;
+        }
+    },  
+    
+    /**
+    * @param {float} opacity
+    */
+    setOpacity:function(opacity) { 
+        if (opacity != undefined) {
+            this.opacity = opacity; 
+        }
+        
+        if (this.div != null) {
+            // for Mozilla and Safari
+            this.div.style.opacity = this.opacity;
+
+            // for IE
+            this.div.style.filter = 'alpha(opacity=' + this.opacity*100 + ')';
+        }
+    },  
+    
+    /**
+    * @param {int} border
+    */
+    setBorder:function(border) { 
+        if (border != undefined) {
+            this.border = border;
+        }
+        
+        if (this.div != null) {
+            this.div.style.border = this.border;
+        }
+    },      
+    
+    /**
+     * @param {String} contentHTML
+     */
+    setContentHTML:function(contentHTML) {
+        if (contentHTML != null) {
+            this.contentHTML = contentHTML;
+        }
+        
+        if (this.contentDiv != null) {
+            this.contentDiv.innerHTML = this.contentHTML;
+        }    
+    },
+    
+
+    
+    /** Do this in a separate function so that subclasses can 
+     *   choose to override it if they wish to deal differently
+     *   with mouse events
+     * 
+     *   Note in the following handler functions that some special
+     *    care is needed to deal correctly with mousing and popups. 
+     *   
+     *   Because the user might select the zoom-rectangle option and
+     *    then drag it over a popup, we need a safe way to allow the
+     *    mousemove and mouseup events to pass through the popup when
+     *    they are initiated from outside.
+     * 
+     *   Otherwise, we want to essentially kill the event propagation
+     *    for all other events, though we have to do so carefully, 
+     *    without disabling basic html functionality, like clicking on 
+     *    hyperlinks or drag-selecting text.
+     */
+     registerEvents:function() {
+        this.events = new OpenLayers.Events(this, this.div, null, true);
+
+        this.events.register("mousedown", this, this.onmousedown);
+        this.events.register("mousemove", this, this.onmousemove);
+        this.events.register("mouseup", this, this.onmouseup);
+        this.events.register("click", this, 
+                             OpenLayers.Util.safeStopPropagation);
+        this.events.register("mouseout", this, this.onmouseout);
+        this.events.register("dblclick", this, 
+                             OpenLayers.Util.safeStopPropagation);
+     },
+
+    /** When mouse goes down within the popup, make a note of
+     *   it locally, and then do not propagate the mousedown 
+     *   (but do so safely so that user can select text inside)
+     * 
+     * @param {Event} evt
+     */
+    onmousedown: function (evt) {
+        this.mousedown = true;
+        OpenLayers.Util.safeStopPropagation(evt);
+    },
+
+    /** If the drag was started within the popup, then 
+     *   do not propagate the mousemove (but do so safely
+     *   so that user can select text inside)
+     * 
+     * @param {Event} evt
+     */
+    onmousemove: function (evt) {
+        if (this.mousedown) {
+            OpenLayers.Util.safeStopPropagation(evt);
+        }
+    },
+
+    /** When mouse comes up within the popup, after going down 
+     *   in it, reset the flag, and then (once again) do not 
+     *   propagate the event, but do so safely so that user can 
+     *   select text inside
+     * 
+     * @param {Event} evt
+     */
+    onmouseup: function (evt) {
+        if (this.mousedown) {
+            this.mousedown = false;
+            OpenLayers.Util.safeStopPropagation(evt);
+        }
+    },
+
+    /** When mouse goes out of the popup set the flag to false so that
+     *   if they let go and then drag back in, we won't be confused.
+     * 
+     * @param {Event} evt
+     * 
+     * @type Boolean
+     */
+    onmouseout: function (evt) {
+        this.mousedown = false;
+    },
+    
+    CLASS_NAME: "OpenLayers.Popup"
+};


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Popup.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/SingleFile.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/SingleFile.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/SingleFile.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,6 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
+ * for the full text of the license. */
+
+_OPENLAYERS_SFL_=true
+


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/SingleFile.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Tile/Image.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Tile/Image.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Tile/Image.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,156 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
+ * for the full text of the license. */
+
+
+/**
+ * @class
+ * 
+ * @requires OpenLayers/Tile.js
+ */
+OpenLayers.Tile.Image = OpenLayers.Class.create();
+OpenLayers.Tile.Image.prototype = 
+  OpenLayers.Class.inherit( OpenLayers.Tile, {
+    
+    /** @type DOMElement img */
+    imgDiv: null,
+
+    /** 
+    * @constructor
+    *
+    * @param {OpenLayers.Grid} layer
+    * @param {OpenLayers.Pixel} position
+    * @param {OpenLayers.Bounds} bounds
+    * @param {String} url
+    * @param {OpenLayers.Size} size
+    */
+    initialize: function(layer, position, bounds, url, size) {
+        OpenLayers.Tile.prototype.initialize.apply(this, arguments);
+    },
+
+    /**
+     * 
+     */
+    destroy: function() {
+        if ((this.imgDiv != null) && (this.imgDiv.parentNode == this.layer.div)) {
+            this.layer.div.removeChild(this.imgDiv);
+        }
+        this.imgDiv = null;
+        OpenLayers.Tile.prototype.destroy.apply(this, arguments);
+    },
+
+    /**
+     * 
+     */
+    draw:function() {
+        OpenLayers.Tile.prototype.draw.apply(this, arguments);
+
+        if (this.imgDiv == null) {
+            this.initImgDiv();
+        }
+        if (this.layer != this.layer.map.baseLayer && this.layer.reproject) {
+            this.bounds = this.getBoundsFromBaseLayer(this.position);
+        }
+
+        this.url = this.layer.getURL(this.bounds);
+        this.imgDiv.style.display = "none";
+        if (this.layer.displayOutsideMaxExtent || (this.layer.maxExtent && 
+            (this.bounds.intersectsBounds(this.layer.maxExtent,false))
+            )) { 
+            if (this.layer.alpha) {
+                OpenLayers.Util.modifyAlphaImageDiv(this.imgDiv,
+                        null, this.position, this.size, this.url);
+            } else {
+                this.imgDiv.src = this.url;
+                OpenLayers.Util.modifyDOMElement(this.imgDiv,
+                        null, this.position, this.size) ;
+            }
+        }
+    },
+
+    /** Clear the tile of any bounds/position-related data so that it can 
+     *   be reused in a new location.
+     */
+    clear: function() {
+        OpenLayers.Tile.prototype.clear.apply(this, arguments);
+        if(this.imgDiv) {
+            this.imgDiv.style.display = "none";
+        }
+    },
+
+    /** 
+     * @param {OpenLayers.Bounds}
+     * @param {OpenLayers.pixel} position
+     * @param {Boolean} redraw
+     */
+    moveTo: function (bounds, position, redraw) {
+        if (this.layer != this.layer.map.baseLayer && this.layer.reproject) {
+            bounds = this.getBoundsFromBaseLayer(position);
+        }
+        this.url = this.layer.getURL(bounds);
+        OpenLayers.Tile.prototype.moveTo.apply(this, arguments);
+    },
+
+    /**
+     * 
+     */
+    initImgDiv: function() {
+        if (this.layer.alpha) {
+            this.imgDiv = OpenLayers.Util.createAlphaImageDiv(null,
+                                                           this.position,
+                                                           this.size,
+                                                           null,
+                                                           "absolute",
+                                                           null,
+                                                           null,
+                                                           null,
+                                                           true);
+        } else {
+            this.imgDiv = OpenLayers.Util.createImage(null,
+                                                      this.position,
+                                                      this.size,
+                                                      null,
+                                                      "absolute",
+                                                      null,
+                                                      null,
+                                                      true);
+        }
+        
+        this.imgDiv.className = 'olTileImage';
+
+        /* checkImgURL *should* pretty predictably get called after the
+             createImage / createAlphaImageDiv onLoad handler */
+
+        OpenLayers.Event.observe( this.imgDiv, "load",
+                        this.checkImgURL.bindAsEventListener(this) );
+
+        this.layer.div.appendChild(this.imgDiv);
+        if(this.layer.opacity != null) {
+            
+            OpenLayers.Util.modifyDOMElement(this.imgDiv, null, null, null,
+                                             null, null, null, 
+                                             this.layer.opacity);
+        }
+    },
+
+    /**
+     * Make sure that the image that just loaded is the one this tile is meant
+     * to display, since panning/zooming might have changed the tile's URL in
+     * the meantime. If the tile URL did change before the image loaded, set
+     * the imgDiv display to 'none', as either (a) it will be reset to visible
+     * when the new URL loads in the image, or (b) we don't want to display
+     * this tile after all because its new bounds are outside our maxExtent.
+     *
+     * @private
+     */
+    checkImgURL: function () {
+        var loaded = this.layer.alpha ? this.imgDiv.firstChild.src : this.imgDiv.src;
+        if (loaded != this.url) {
+            this.imgDiv.style.display = "none";
+        }
+    },
+
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.Tile.Image"
+  }
+);


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Tile/Image.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Tile/WFS.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Tile/WFS.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Tile/WFS.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,134 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
+ * for the full text of the license. */
+
+ 
+/**
+ * @class
+ * 
+ * @requires OpenLayers/Tile.js
+ */
+OpenLayers.Tile.WFS = OpenLayers.Class.create();
+OpenLayers.Tile.WFS.prototype = 
+  OpenLayers.Class.inherit( OpenLayers.Tile, {
+
+    /** @type Array(OpenLayers.Feature)*/ 
+    features: null,
+
+    /** @type Array(String) */
+    urls: null,
+    
+    /** 
+    * @constructor
+    *
+    * @param {OpenLayers.Layer} layer
+    * @param {OpenLayers.Pixel} position
+    * @param {OpenLayers.Bounds} bounds
+    * @param {Array} urls
+    * @param {OpenLayers.Size} size
+    */
+    initialize: function(layer, position, bounds, urls, size) {
+        var newArguments = arguments;
+        newArguments = [layer, position, bounds, null, size];
+        OpenLayers.Tile.prototype.initialize.apply(this, newArguments);
+        this.urls = urls;        
+        this.features = new Array();
+    },
+
+    /**
+     * 
+     */
+    destroy: function() {
+        OpenLayers.Tile.prototype.destroy.apply(this, arguments);
+        this.destroyAllFeatures();
+        this.features = null;
+        this.urls = null;
+    },
+
+    /** Clear the tile of any bounds/position-related data so that it can 
+     *   be reused in a new location.
+     */
+    clear: function() {
+        OpenLayers.Tile.prototype.clear.apply(this, arguments);
+        this.destroyAllFeatures();
+    },
+    
+    /**
+     * 
+     */
+    draw:function() {
+        if (this.drawn) {
+            this.clear();
+        }
+        OpenLayers.Tile.prototype.draw.apply(this, arguments);
+        if (this.layer.displayOutsideMaxExtent || (this.layer.maxExtent && 
+            this.layer.maxExtent.intersectsBounds(this.bounds, false))) { 
+            this.loadFeaturesForRegion(this.requestSuccess);
+        }         
+    },
+
+    /** get the full request string from the ds and the tile params 
+    *     and call the AJAX loadURL(). 
+    *
+    *     input are function pointers for what to do on success and failure.
+    * 
+    * @param {function} success
+    * @param {function} failure
+    */
+    loadFeaturesForRegion:function(success, failure) {
+
+        if (this.urls != null) {
+        
+            for(var i=0; i < this.urls.length; i++) {
+                var params = { BBOX:this.bounds.toBBOX() };
+                var url = this.urls[i] + "&" + 
+                          OpenLayers.Util.getParameterString(params);
+                OpenLayers.loadURL(url, null, this, success, failure);
+            }
+        }
+    },
+    
+    /** Return from AJAX request
+    *
+    * @param {} request
+    */
+    requestSuccess:function(request) {
+        var doc = request.responseXML;
+        
+        if (!doc || request.fileType!="XML") {
+            doc = OpenLayers.parseXMLString(request.responseText);
+        }
+        
+        var resultFeatures = OpenLayers.Ajax.getElementsByTagNameNS(doc, "http://www.opengis.net/gml","gml", "featureMember");
+        this.addResults(resultFeatures);
+    },
+
+    /**
+     * @param {Object} results
+     */
+    addResults: function(results) {
+        for (var i=0; i < results.length; i++) {
+            var feature = new this.layer.featureClass(this.layer, 
+                                                      results[i]);
+            this.features.push(feature);
+        }
+    },
+
+    /** Iterate through and call destroy() on each feature, removing it from
+     *   the local array
+     * 
+     * @private
+     */
+    destroyAllFeatures: function() {
+        while(this.features.length > 0) {
+            var feature = this.features.shift();
+            feature.destroy();
+        }
+    },
+    
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.Tile.WFS"
+  }
+);
+
+


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Tile/WFS.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Tile.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Tile.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Tile.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,124 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
+ * for the full text of the license. */
+
+
+/*
+ * @class 
+ * 
+ * This is a class designed to designate a single tile, however
+ * it is explicitly designed to do relatively little. Tiles store information
+ * about themselves -- such as the URL that they are related to, and their 
+ * size - but do not add themselves to the layer div automatically, for 
+ * example.
+ */
+OpenLayers.Tile = OpenLayers.Class.create();
+OpenLayers.Tile.prototype = {
+    
+    /** @type String */
+    id: null,
+    
+    /** @type OpenLayers.Layer */
+    layer: null,
+    
+    /** @type String url of the request */
+    url:null,
+
+    /** @type OpenLayers.Bounds */
+    bounds:null,
+    
+    /** @type OpenLayers.Size */
+    size:null,
+    
+    /** Top Left pixel of the tile
+    * @type OpenLayers.Pixel */
+    position:null,
+
+    /** @type Boolean */
+    drawn: false,
+
+    /**
+    * @constructor
+    *
+    * @param {OpenLayers.Layer} layer
+    * @param {OpenLayers.Pixel} position
+    * @param {OpenLayers.Bounds} bounds
+    * @param {String} url
+    * @param {OpenLayers.Size} size
+    */   
+    initialize: function(layer, position, bounds, url, size) {
+        this.layer = layer;
+        this.position = position;
+        this.bounds = bounds;
+        this.url = url;
+        this.size = size;
+
+        //give the tile a unique id based on its BBOX.
+        this.id = OpenLayers.Util.createUniqueID("Tile_");
+    },
+    
+    /** nullify references to prevent circular references and memory leaks
+    */
+    destroy:function() {
+        this.layer  = null;
+        this.bounds = null;
+        this.size = null;
+        this.position = null;
+    },
+
+    /**
+    */
+    draw:function() {
+        this.drawn = true;
+    },
+    
+    /** 
+     * @param {OpenLayers.Bounds}
+     * @param {OpenLayers.pixel} position
+     * @param {Boolean} redraw Redraw tile after moving? 
+     *                         Default is true
+     */
+    moveTo: function (bounds, position, redraw) {
+        if (redraw == null) {
+            redraw = true;
+        }
+
+        this.clear();
+        this.bounds = bounds.clone();
+        this.position = position.clone();
+        if (redraw) {
+            this.draw();
+        }
+    },
+
+    /** Clear the tile of any bounds/position-related data so that it can 
+     *   be reused in a new location.
+     */
+    clear: function() {
+        this.drawn = false;
+    },
+    
+    getBoundsFromBaseLayer: function(position) {
+        var topLeft = this.layer.map.getLonLatFromLayerPx(this.position); 
+        var bottomRightPx = this.position.clone();
+        bottomRightPx.x += this.size.w;
+        bottomRightPx.y += this.size.h;
+        var bottomRight = this.layer.map.getLonLatFromLayerPx(bottomRightPx); 
+        // Handle the case where the base layer wraps around the date line.
+        // Google does this, and it breaks WMS servers to request bounds in that fashion.  
+        if (topLeft.lon > bottomRight.lon) {
+            if (topLeft.lon < 0) {
+                topLeft.lon = -180 - (topLeft.lon+180);
+            } else {
+                bottomRight.lon = 180+bottomRight.lon+180;
+            }        
+        }             
+        bounds = new OpenLayers.Bounds(topLeft.lon, bottomRight.lat, bottomRight.lon, topLeft.lat);  
+        return bounds;
+    },        
+
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.Tile"
+};
+
+


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Tile.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Util.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Util.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Util.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,694 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
+ * for the full text of the license. */
+
+
+/**
+ * @class
+ */
+OpenLayers.Util = new Object();
+
+/* from Prototype.js */
+if ($ == null) {
+    var $ = function () {
+      var elements = new Array();
+
+      for (var i = 0; i < arguments.length; i++) {
+        var element = arguments[i];
+        if (typeof element == 'string')
+          element = document.getElementById(element);
+
+        if (arguments.length == 1)
+          return element;
+
+        elements.push(element);
+      }
+
+      return elements;
+    }
+} 
+
+/* from Prototype.js */
+OpenLayers.Util.extend = function(destination, source) {
+    for (property in source) {
+      destination[property] = source[property];
+    }
+    return destination;
+};
+
+
+/** Remove an object from an array. Iterates through the array
+*    to find the item, then removes it.
+*
+* @param {Object} item
+* 
+* @returns A reference to the array
+* @type Array
+*/
+OpenLayers.Util.removeItem = function(array, item) {
+    for(var i=0; i < array.length; i++) {
+        if(array[i] == item) {
+            array.splice(i,1);
+            //break;more than once??
+        }
+    }
+    return array;
+};
+
+/**
+*/
+OpenLayers.Util.clearArray = function(array) {
+    array.length = 0;
+};
+
+/** Seems to exist already in FF, but not in MOZ.
+ * 
+ * @param {Array} array
+ * @param {Object} obj
+ */
+OpenLayers.Util.indexOf = function(array, obj) {
+
+    for(var i=0; i < array.length; i++) {
+        if (array[i] == obj) return i;
+    }
+    return -1;   
+};
+
+
+
+/**
+ * @param {String} id
+ * @param {OpenLayers.Pixel} px
+ * @param {OpenLayers.Size} sz
+ * @param {String} position
+ * @param {String} border
+ * @param {String} overflow
+ * @param {float} opacity Fractional value (0.0 - 1.0)
+ */
+OpenLayers.Util.modifyDOMElement = function(element, id, px, sz, position, 
+                                            border, overflow, opacity) {
+
+    if (id) {
+        element.id = id;
+    }
+    if (px) {
+        element.style.left = px.x + "px";
+        element.style.top = px.y + "px";
+    }
+    if (sz) {
+        element.style.width = sz.w + "px";
+        element.style.height = sz.h + "px";
+    }
+    if (position) {
+        element.style.position = position;
+    }
+    if (border) {
+        element.style.border = border;
+    }
+    if (overflow) {
+        element.style.overflow = overflow;
+    }
+    if (opacity) {
+        element.style.opacity = opacity;
+        element.style.filter = 'alpha(opacity=' + (opacity * 100) + ')';
+    }
+};
+
+/** 
+* zIndex is NOT set
+*
+* @param {String} id
+* @param {OpenLayers.Pixel} px
+* @param {OpenLayers.Size} sz
+* @param {String} imgURL
+* @param {String} position
+* @param {String} border
+* @param {String} overflow
+* @param {float} opacity Fractional value (0.0 - 1.0)
+*
+* @returns A DOM Div created with the specified attributes.
+* @type DOMElement
+*/
+OpenLayers.Util.createDiv = function(id, px, sz, imgURL, position, 
+                                     border, overflow, opacity) {
+
+    var dom = document.createElement('div');
+
+    if (imgURL) {
+        dom.style.backgroundImage = 'url(' + imgURL + ')';
+    }
+
+    //set generic properties
+    if (!id) {
+        id = OpenLayers.Util.createUniqueID("OpenLayersDiv");
+    }
+    if (!position) {
+        position = "absolute";
+    }
+    OpenLayers.Util.modifyDOMElement(dom, id, px, sz, position, 
+                                     border, overflow, opacity);
+
+    return dom;
+};
+
+/** 
+* @param {String} id
+* @param {OpenLayers.Pixel} px
+* @param {OpenLayers.Size} sz
+* @param {String} imgURL
+* @param {String} position
+* @param {String} border
+* @param {Boolean} delayDisplay
+* @param {float} opacity Fractional value (0.0 - 1.0)
+*
+* @returns A DOM Image created with the specified attributes.
+* @type DOMElement
+*/
+OpenLayers.Util.createImage = function(id, px, sz, imgURL, position, border,
+                                       opacity, delayDisplay) {
+
+    image = document.createElement("img");
+
+    if(delayDisplay) {
+        image.style.display = "none";
+        OpenLayers.Event.observe(image, "load", 
+                      OpenLayers.Util.onImageLoad.bindAsEventListener(image));
+        OpenLayers.Event.observe(image, "error", 
+                      OpenLayers.Util.onImageLoadError.bindAsEventListener(image));
+        
+    }
+    
+    //set special properties
+    image.style.alt = id;
+    image.galleryImg = "no";
+    if (imgURL) {
+        image.src = imgURL;
+    }
+
+    //set generic properties
+    if (!id) {
+        id = OpenLayers.Util.createUniqueID("OpenLayersDiv");
+    }
+    if (!position) {
+        position = "relative";
+    }
+    OpenLayers.Util.modifyDOMElement(image, id, px, sz, position, 
+                                     border, null, opacity);
+
+        
+    return image;
+};
+
+/**
+ * @deprecated -- Use OpenLayers.Util.modifyDOMElement() or 
+ *                    OpenLayers.Util.modifyAlphaImageDiv()
+ * 
+ * Set the opacity of a DOM Element
+ * Note that for this function to work in IE, elements must "have layout"
+ * according to:
+ * http://msdn.microsoft.com/workshop/author/dhtml/reference/properties/haslayout.asp
+ *
+ * @param {DOMElement} element Set the opacity on this DOM element
+ * @param {Float} opacity Opacity value (0.0 - 1.0)
+ */
+OpenLayers.Util.setOpacity = function(element, opacity) {
+    OpenLayers.Util.modifyDOMElement(element, null, null, null,
+                                     null, null, null, opacity);
+}
+
+OpenLayers.Util.onImageLoad = function() {
+    this.style.backgroundColor = null;
+    this.style.display = "";  
+};
+
+OpenLayers.Util.onImageLoadErrorColor = "pink";
+
+OpenLayers.Util.onImageLoadError = function() {
+    this.style.backgroundColor = OpenLayers.Util.onImageLoadErrorColor;
+    this.style.display = "";
+};
+
+
+OpenLayers.Util.alphaHack = function() {
+    var arVersion = navigator.appVersion.split("MSIE");
+    var version = parseFloat(arVersion[1]);
+    var filter = false;
+    
+    // IEs4Lin dies when trying to access document.body.filters, because 
+    // the property is there, but requires a DLL that can't be provided. This
+    // means that we need to wrap this in a try/catch so that this can
+    // continue.
+    
+    try { 
+        filter = document.body.filters;
+    } catch (e) {
+    }    
+    
+    return ( filter &&
+                      (version >= 5.5) && (version < 7) );
+}
+
+/** 
+* @param {DOMElement} div Div containing Alpha-adjusted Image
+* @param {String} id
+* @param {OpenLayers.Pixel} px
+* @param {OpenLayers.Size} sz
+* @param {String} imgURL
+* @param {String} position
+* @param {String} border
+* @param {String} sizing 'crop', 'scale', or 'image'. Default is "scale"
+* @param {float} opacity Specified as fraction (0.4, etc)
+*/ 
+OpenLayers.Util.modifyAlphaImageDiv = function(div, id, px, sz, imgURL, 
+                                               position, border, sizing, 
+                                               opacity) {
+
+    OpenLayers.Util.modifyDOMElement(div, id, px, sz);
+
+    var img = div.childNodes[0];
+
+    if (imgURL) {
+        img.src = imgURL;
+    }
+    OpenLayers.Util.modifyDOMElement(img, div.id + "_innerImage", null, sz, 
+                                     "relative", border);
+    if (opacity) {
+        div.style.opacity = opacity;
+        div.style.filter = 'alpha(opacity=' + (opacity * 100) + ')';
+    }
+    
+    if (OpenLayers.Util.alphaHack()) {
+
+        div.style.display = "inline-block";
+        if (sizing == null) {
+            sizing = "scale";
+        }
+        
+        div.style.filter = "progid:DXImageTransform.Microsoft" +
+                           ".AlphaImageLoader(src='" + img.src + "', " +
+                           "sizingMethod='" + sizing + "')";
+        if (div.style.opacity) {
+            div.style.filter += " alpha(opacity=" + div.style.opacity * 100 + ")";
+        }
+
+        img.style.filter = "progid:DXImageTransform.Microsoft" +
+                                ".Alpha(opacity=0)";
+    }
+};
+
+/** 
+* @param {String} id
+* @param {OpenLayers.Pixel} px
+* @param {OpenLayers.Size} sz
+* @param {String} imgURL
+* @param {String} position
+* @param {String} border
+* @param {String} sizing 'crop', 'scale', or 'image'. Default is "scale"
+* @param {Boolean} delayDisplay
+*
+* @returns A DOM Div created with a DOM Image inside it. If the hack is 
+*           needed for transparency in IE, it is added.
+* @type DOMElement
+*/ 
+OpenLayers.Util.createAlphaImageDiv = function(id, px, sz, imgURL, 
+                                               position, border, sizing, 
+                                               opacity, delayDisplay) {
+    
+    var div = OpenLayers.Util.createDiv();
+    var img = OpenLayers.Util.createImage(null, null, null, null, null, null, 
+                                          false);
+    div.appendChild(img);
+
+    if (delayDisplay) {
+        img.style.display = "none";
+        OpenLayers.Event.observe(img, "load",
+                      OpenLayers.Util.onImageLoad.bindAsEventListener(div));
+        OpenLayers.Event.observe(img, "error",
+                      OpenLayers.Util.onImageLoadError.bindAsEventListener(div));
+    }
+
+    OpenLayers.Util.modifyAlphaImageDiv(div, id, px, sz, imgURL, position, 
+                                        border, sizing, opacity);
+    
+    return div;
+};
+
+
+/** Creates a new hashtable and copies over all the keys from the 
+*    passed-in object, but storing them under an uppercased
+*    version of the key at which they were stored.
+* 
+* @param {Object} object
+*
+* @returns A new Object with all the same keys but uppercased
+* @type Object
+*/
+OpenLayers.Util.upperCaseObject = function (object) {
+    var uObject = new Object();
+    for (var key in object) {
+        uObject[key.toUpperCase()] = object[key];
+    }
+    return uObject;
+};
+
+/** Takes a hashtable and copies any keys that don't exist from
+*   another hashtable, by analogy with OpenLayers.Util.extend() from
+*   Prototype.js.
+*
+* @param {Object} to
+* @param {Object} from
+*/
+OpenLayers.Util.applyDefaults = function (to, from) {
+    for (var key in from) {
+        if (to[key] == null) {
+            to[key] = from[key];
+        }
+    }
+};
+
+/**
+* @param {Object} params
+*
+* @returns a concatenation of the properties of an object in 
+*    http parameter notation. 
+*    (ex. <i>"key1=value1&key2=value2&key3=value3"</i>)
+* @type String
+*/
+OpenLayers.Util.getParameterString = function(params) {
+    paramsArray = new Array();
+    
+    for (var key in params) {
+        var value = params[key];
+        if ((value != null) && (typeof value != 'function')) {
+            paramsArray.push(key + "=" + value);
+        }
+    }
+    
+    return paramsArray.join("&");
+};
+
+/** 
+* @returns The fully formatted image location string
+* @type String
+*/
+
+OpenLayers.ImgPath = '';
+OpenLayers.Util.getImagesLocation = function() {
+    return OpenLayers.ImgPath || (OpenLayers._getScriptLocation() + "img/");
+};
+
+/* Originally from Prototype */
+
+OpenLayers.Util.Try = function() {
+    var returnValue;
+
+    for (var i = 0; i < arguments.length; i++) {
+      var lambda = arguments[i];
+      try {
+        returnValue = lambda();
+        break;
+      } catch (e) {}
+    }
+
+    return returnValue;
+}
+
+
+/** These could/should be made namespace aware?
+*
+* @param {} p
+* @param {str} tagName
+*
+* @return {Array}
+*/
+OpenLayers.Util.getNodes=function(p, tagName) {
+    var nodes = OpenLayers.Util.Try(
+        function () {
+            return OpenLayers.Util._getNodes(p.documentElement.childNodes,
+                                            tagName);
+        },
+        function () {
+            return OpenLayers.Util._getNodes(p.childNodes, tagName);
+        }
+    );
+    return nodes;
+};
+
+/**
+* @param {Array} nodes
+* @param {str} tagName
+*
+* @return {Array}
+*/
+OpenLayers.Util._getNodes=function(nodes, tagName) {
+    var retArray = new Array();
+    for (var i=0;i<nodes.length;i++) {
+        if (nodes[i].nodeName==tagName) {
+            retArray.push(nodes[i]);
+        }
+    }
+
+    return retArray;
+};
+
+
+
+/**
+* @param {} parent
+* @param {str} item
+* @param {int} index
+*
+* @return {str}
+*/
+OpenLayers.Util.getTagText = function (parent, item, index) {
+    var result = OpenLayers.Util.getNodes(parent, item);
+    if (result && (result.length > 0))
+    {
+        if (!index) {
+            index=0;
+        }
+        if (result[index].childNodes.length > 1) {
+            return result.childNodes[1].nodeValue; 
+        }
+        else if (result[index].childNodes.length == 1) {
+            return result[index].firstChild.nodeValue; 
+        }
+    } else { 
+        return ""; 
+    }
+};
+
+/**
+ * @param {XMLNode} node
+ * 
+ * @returns The text value of the given node, without breaking in firefox or IE
+ * @type String
+ */
+OpenLayers.Util.getXmlNodeValue = function(node) {
+    var val = null;
+    OpenLayers.Util.Try( 
+        function() {
+            val = node.text;
+            if (!val)
+                val = node.textContent;
+            if (!val)
+                val = node.firstChild.nodeValue;
+        }, 
+        function() {
+            val = node.textContent;
+        }); 
+    return val;
+};
+
+/** 
+* @param {Event} evt
+* @param {HTMLDivElement} div
+*
+* @return {boolean}
+*/
+OpenLayers.Util.mouseLeft = function (evt, div) {
+    // start with the element to which the mouse has moved
+    var target = (evt.relatedTarget) ? evt.relatedTarget : evt.toElement;
+    // walk up the DOM tree.
+    while (target != div && target != null) {
+        target = target.parentNode;
+    }
+    // if the target we stop at isn't the div, then we've left the div.
+    return (target != div);
+};
+
+OpenLayers.Util.rad = function(x) {return x*Math.PI/180;};
+OpenLayers.Util.distVincenty=function(p1, p2) {
+    var a = 6378137, b = 6356752.3142,  f = 1/298.257223563;
+    var L = OpenLayers.Util.rad(p2.lon - p1.lon);
+    var U1 = Math.atan((1-f) * Math.tan(OpenLayers.Util.rad(p1.lat)));
+    var U2 = Math.atan((1-f) * Math.tan(OpenLayers.Util.rad(p2.lat)));
+    var sinU1 = Math.sin(U1), cosU1 = Math.cos(U1);
+    var sinU2 = Math.sin(U2), cosU2 = Math.cos(U2);
+    var lambda = L, lambdaP = 2*Math.PI;
+    var iterLimit = 20;
+    while (Math.abs(lambda-lambdaP) > 1e-12 && --iterLimit>0) {
+        var sinLambda = Math.sin(lambda), cosLambda = Math.cos(lambda);
+        var sinSigma = Math.sqrt((cosU2*sinLambda) * (cosU2*sinLambda) +
+        (cosU1*sinU2-sinU1*cosU2*cosLambda) * (cosU1*sinU2-sinU1*cosU2*cosLambda));
+        if (sinSigma==0) return 0;  // co-incident points
+        var cosSigma = sinU1*sinU2 + cosU1*cosU2*cosLambda;
+        var sigma = Math.atan2(sinSigma, cosSigma);
+        var alpha = Math.asin(cosU1 * cosU2 * sinLambda / sinSigma);
+        var cosSqAlpha = Math.cos(alpha) * Math.cos(alpha);
+        var cos2SigmaM = cosSigma - 2*sinU1*sinU2/cosSqAlpha;
+        var C = f/16*cosSqAlpha*(4+f*(4-3*cosSqAlpha));
+        lambdaP = lambda;
+        lambda = L + (1-C) * f * Math.sin(alpha) *
+        (sigma + C*sinSigma*(cos2SigmaM+C*cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)));
+    }
+    if (iterLimit==0) return NaN  // formula failed to converge
+    var uSq = cosSqAlpha * (a*a - b*b) / (b*b);
+    var A = 1 + uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq)));
+    var B = uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq)));
+    var deltaSigma = B*sinSigma*(cos2SigmaM+B/4*(cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)-
+        B/6*cos2SigmaM*(-3+4*sinSigma*sinSigma)*(-3+4*cos2SigmaM*cos2SigmaM)));
+    var s = b*A*(sigma-deltaSigma);
+    var d = s.toFixed(3)/1000; // round to 1mm precision
+    return d;
+};
+    
+OpenLayers.Util.getArgs = function() {
+    var args = new Object();
+    var query = location.search.substring(1);  // Get query string.
+    var pairs = query.split("&");              // Break at ampersand. //+pjl
+
+    for(var i = 0; i < pairs.length; i++) {
+        var pos = pairs[i].indexOf('=');       // Look for "name=value".
+        if (pos == -1) continue;               // If not found, skip.
+        var argname = pairs[i].substring(0,pos);  // Extract the name.
+        var value = pairs[i].substring(pos+1); // Extract the value.
+        args[argname] = unescape(value);          // Store as a property.
+    }
+    return args;                               // Return the object.
+};
+
+/**
+ * @param {String} prefix String to prefix random id. If null, default
+ *                         is "id_"
+ * 
+ * @returns A unique id string, built on the passed in prefix
+ * @type String
+ */
+OpenLayers.Util.createUniqueID = function(prefix) {
+    if (prefix == null) {
+        prefix = "id_";
+    }
+    return prefix + Math.round(Math.random() * 10000);        
+};
+
+/** Constant inches per unit 
+ *    -- borrowed from MapServer mapscale.c
+ * 
+ * @type Object */
+OpenLayers.INCHES_PER_UNIT = { 
+    'inches': 1.0,
+    'ft': 12.0,
+    'mi': 63360.0,
+    'm': 39.3701,
+    'km': 39370.1,
+    'dd': 4374754
+};
+OpenLayers.INCHES_PER_UNIT["in"]= OpenLayers.INCHES_PER_UNIT.inches;
+OpenLayers.INCHES_PER_UNIT["degrees"] = OpenLayers.INCHES_PER_UNIT.dd;
+
+/** A sensible default 
+ * @type int */
+OpenLayers.DOTS_PER_INCH = 72;
+
+/**
+ * @param {float} scale
+ * 
+ * @returns A normalized scale value, in 1 / X format. 
+ *          This means that if a value less than one ( already 1/x) is passed
+ *          in, it just returns scale directly. Otherwise, it returns 
+ *          1 / scale
+ * @type float
+ */
+OpenLayers.Util.normalizeScale = function (scale) {
+    var normScale = (scale > 1.0) ? (1.0 / scale) 
+                                  : scale;
+    return normScale;
+};
+
+/**
+ * @param {float} scale
+ * @param {String} units Index into OpenLayers.INCHES_PER_UNIT hashtable.
+ *                       Default is degrees
+ * 
+ * @returns The corresponding resolution given passed-in scale and unit 
+ *          parameters.
+ * @type float
+ */
+OpenLayers.Util.getResolutionFromScale = function (scale, units) {
+
+    if (units == null) {
+        units = "degrees";
+    }
+
+    var normScale = OpenLayers.Util.normalizeScale(scale);
+
+    var resolution = 1 / (normScale * OpenLayers.INCHES_PER_UNIT[units]
+                                    * OpenLayers.DOTS_PER_INCH);
+    return resolution;
+};
+
+/**
+ * @param {float} resolution
+ * @param {String} units Index into OpenLayers.INCHES_PER_UNIT hashtable.
+ *                       Default is degrees
+ * 
+ * @returns The corresponding scale given passed-in resolution and unit 
+ *          parameters.
+ * @type float
+ */
+OpenLayers.Util.getScaleFromResolution = function (resolution, units) {
+
+    if (units == null) {
+        units = "degrees";
+    }
+
+    var scale = resolution * OpenLayers.INCHES_PER_UNIT[units] *
+                    OpenLayers.DOTS_PER_INCH;
+    return scale;
+};
+
+/** Safely stop the propagation of an event *without* preventing
+ *   the default browser action from occurring.
+ * 
+ * @param {Event} evt
+ */
+OpenLayers.Util.safeStopPropagation = function(evt) {
+    if (evt.stopPropagation) {
+        evt.stopPropagation();
+    } 
+    evt.cancelBubble = true;    
+};
+
+OpenLayers.Util.pagePosition = function(forElement) {
+    var valueT = 0, valueL = 0;
+
+    var element = forElement;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+
+      // Safari fix
+      if (element.offsetParent==document.body)
+        if (OpenLayers.Element.getStyle(element,'position')=='absolute') break;
+
+    } while (element = element.offsetParent);
+
+    element = forElement;
+    do {
+      valueT -= element.scrollTop  || 0;
+      valueL -= element.scrollLeft || 0;
+    } while (element = element.parentNode);
+
+    return [valueL, valueT];
+};


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers/Util.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,124 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
+ * for the full text of the license. */
+
+////
+/// This blob sucks in all the files in uncompressed form for ease of use
+///
+
+OpenLayers = new Object();
+
+OpenLayers._scriptName = ( 
+    typeof(_OPENLAYERS_SFL_) == "undefined" ? "lib/OpenLayers.js" 
+                                            : "OpenLayers.js" );
+
+OpenLayers._getScriptLocation = function () {
+    var scriptLocation = "";
+    var SCRIPT_NAME = OpenLayers._scriptName;
+ 
+    var scripts = document.getElementsByTagName('script');
+    for (var i = 0; i < scripts.length; i++) {
+        var src = scripts[i].getAttribute('src');
+        if (src) {
+            var index = src.lastIndexOf(SCRIPT_NAME); 
+            // is it found, at the end of the URL?
+            if ((index > -1) && (index + SCRIPT_NAME.length == src.length)) {  
+                scriptLocation = src.slice(0, -SCRIPT_NAME.length);
+                break;
+            }
+        }
+    }
+    return scriptLocation;
+}
+
+/*
+  `_OPENLAYERS_SFL_` is a flag indicating this file is being included
+  in a Single File Library build of the OpenLayers Library.
+
+  When we are *not* part of a SFL build we dynamically include the
+  OpenLayers library code.
+
+  When we *are* part of a SFL build we do not dynamically include the 
+  OpenLayers library code as it will be appended at the end of this file.
+*/
+if (typeof(_OPENLAYERS_SFL_) == "undefined") {
+    /*
+      The original code appeared to use a try/catch block
+      to avoid polluting the global namespace,
+      we now use a anonymous function to achieve the same result.
+     */
+    (function() {
+    var jsfiles=new Array(
+        "OpenLayers/BaseTypes.js",
+        "OpenLayers/Util.js",
+        "Rico/Corner.js",
+        "Rico/Color.js",
+        "OpenLayers/Ajax.js",
+        "OpenLayers/Events.js",
+        "OpenLayers/Map.js",
+        "OpenLayers/Layer.js",
+        "OpenLayers/Icon.js",
+        "OpenLayers/Marker.js",
+        "OpenLayers/Marker/Box.js",
+        "OpenLayers/Popup.js",
+        "OpenLayers/Tile.js",
+        "OpenLayers/Feature.js",
+        "OpenLayers/Feature/WFS.js",
+        "OpenLayers/Tile/Image.js",
+        "OpenLayers/Tile/WFS.js",
+        "OpenLayers/Layer/Image.js",
+        "OpenLayers/Layer/EventPane.js",
+        "OpenLayers/Layer/FixedZoomLevels.js",
+        "OpenLayers/Layer/Google.js",
+        "OpenLayers/Layer/VirtualEarth.js",
+        "OpenLayers/Layer/Yahoo.js",
+        "OpenLayers/Layer/HTTPRequest.js",
+        "OpenLayers/Layer/Grid.js",
+        "OpenLayers/Layer/MapServer.js",
+        "OpenLayers/Layer/KaMap.js",
+        "OpenLayers/Layer/MultiMap.js",
+        "OpenLayers/Layer/Markers.js",
+        "OpenLayers/Layer/Text.js",
+        "OpenLayers/Layer/WorldWind.js",
+        "OpenLayers/Layer/WMS.js",
+        "OpenLayers/Layer/WFS.js",
+        "OpenLayers/Layer/WMS/Untiled.js",
+        "OpenLayers/Layer/GeoRSS.js",
+        "OpenLayers/Layer/Boxes.js",
+        "OpenLayers/Layer/Canvas.js",
+        "OpenLayers/Popup/Anchored.js",
+        "OpenLayers/Popup/AnchoredBubble.js",
+        "OpenLayers/Control.js",
+        "OpenLayers/Control/MouseDefaults.js",
+        "OpenLayers/Control/MouseToolbar.js",
+        "OpenLayers/Control/MousePosition.js",
+        "OpenLayers/Control/OverviewMap.js",
+        "OpenLayers/Control/KeyboardDefaults.js",
+        "OpenLayers/Control/PanZoom.js",
+        "OpenLayers/Control/PanZoomBar.js",
+        "OpenLayers/Control/ArgParser.js",
+        "OpenLayers/Control/Permalink.js",
+        "OpenLayers/Control/Scale.js",
+        "OpenLayers/Control/LayerSwitcher.js"
+    ); // etc.
+
+    var allScriptTags = "";
+    var host = OpenLayers._getScriptLocation() + "lib/";
+
+    for (var i = 0; i < jsfiles.length; i++) {
+        if (/MSIE/.test(navigator.userAgent) || /Safari/.test(navigator.userAgent)) {
+            var currentScriptTag = "<script src='" + host + jsfiles[i] + "'></script>"; 
+            allScriptTags += currentScriptTag;
+        } else {
+            var s = document.createElement("script");
+            s.src = host + jsfiles[i];
+            var h = document.getElementsByTagName("head").length ? 
+                       document.getElementsByTagName("head")[0] : 
+                       document.body;
+            h.appendChild(s);
+        }
+    }
+    if (allScriptTags) document.write(allScriptTags);
+    })();
+}
+OpenLayers.VERSION_NUMBER="$Revision: 1865 $";


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/OpenLayers.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/Rico/Color.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/Rico/Color.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/Rico/Color.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,232 @@
+Rico.Color = OpenLayers.Class.create();
+
+Rico.Color.prototype = {
+
+   initialize: function(red, green, blue) {
+      this.rgb = { r: red, g : green, b : blue };
+   },
+
+   setRed: function(r) {
+      this.rgb.r = r;
+   },
+
+   setGreen: function(g) {
+      this.rgb.g = g;
+   },
+
+   setBlue: function(b) {
+      this.rgb.b = b;
+   },
+
+   setHue: function(h) {
+
+      // get an HSB model, and set the new hue...
+      var hsb = this.asHSB();
+      hsb.h = h;
+
+      // convert back to RGB...
+      this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, hsb.b);
+   },
+
+   setSaturation: function(s) {
+      // get an HSB model, and set the new hue...
+      var hsb = this.asHSB();
+      hsb.s = s;
+
+      // convert back to RGB and set values...
+      this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, hsb.b);
+   },
+
+   setBrightness: function(b) {
+      // get an HSB model, and set the new hue...
+      var hsb = this.asHSB();
+      hsb.b = b;
+
+      // convert back to RGB and set values...
+      this.rgb = Rico.Color.HSBtoRGB( hsb.h, hsb.s, hsb.b );
+   },
+
+   darken: function(percent) {
+      var hsb  = this.asHSB();
+      this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, Math.max(hsb.b - percent,0));
+   },
+
+   brighten: function(percent) {
+      var hsb  = this.asHSB();
+      this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, Math.min(hsb.b + percent,1));
+   },
+
+   blend: function(other) {
+      this.rgb.r = Math.floor((this.rgb.r + other.rgb.r)/2);
+      this.rgb.g = Math.floor((this.rgb.g + other.rgb.g)/2);
+      this.rgb.b = Math.floor((this.rgb.b + other.rgb.b)/2);
+   },
+
+   isBright: function() {
+      var hsb = this.asHSB();
+      return this.asHSB().b > 0.5;
+   },
+
+   isDark: function() {
+      return ! this.isBright();
+   },
+
+   asRGB: function() {
+      return "rgb(" + this.rgb.r + "," + this.rgb.g + "," + this.rgb.b + ")";
+   },
+
+   asHex: function() {
+      return "#" + this.rgb.r.toColorPart() + this.rgb.g.toColorPart() + this.rgb.b.toColorPart();
+   },
+
+   asHSB: function() {
+      return Rico.Color.RGBtoHSB(this.rgb.r, this.rgb.g, this.rgb.b);
+   },
+
+   toString: function() {
+      return this.asHex();
+   }
+
+};
+
+Rico.Color.createFromHex = function(hexCode) {
+  if(hexCode.length==4) {
+    var shortHexCode = hexCode; 
+    var hexCode = '#';
+    for(var i=1;i<4;i++) hexCode += (shortHexCode.charAt(i) + 
+shortHexCode.charAt(i));
+  }
+   if ( hexCode.indexOf('#') == 0 )
+      hexCode = hexCode.substring(1);
+   var red   = hexCode.substring(0,2);
+   var green = hexCode.substring(2,4);
+   var blue  = hexCode.substring(4,6);
+   return new Rico.Color( parseInt(red,16), parseInt(green,16), parseInt(blue,16) );
+}
+
+/**
+ * Factory method for creating a color from the background of
+ * an HTML element.
+ */
+Rico.Color.createColorFromBackground = function(elem) {
+
+   var actualColor = RicoUtil.getElementsComputedStyle($(elem), "backgroundColor", "background-color");
+
+   if ( actualColor == "transparent" && elem.parentNode )
+      return Rico.Color.createColorFromBackground(elem.parentNode);
+
+   if ( actualColor == null )
+      return new Rico.Color(255,255,255);
+
+   if ( actualColor.indexOf("rgb(") == 0 ) {
+      var colors = actualColor.substring(4, actualColor.length - 1 );
+      var colorArray = colors.split(",");
+      return new Rico.Color( parseInt( colorArray[0] ),
+                            parseInt( colorArray[1] ),
+                            parseInt( colorArray[2] )  );
+
+   }
+   else if ( actualColor.indexOf("#") == 0 ) {
+      return Rico.Color.createFromHex(actualColor);
+   }
+   else
+      return new Rico.Color(255,255,255);
+}
+
+Rico.Color.HSBtoRGB = function(hue, saturation, brightness) {
+
+   var red   = 0;
+    var green = 0;
+    var blue  = 0;
+
+   if (saturation == 0) {
+      red = parseInt(brightness * 255.0 + 0.5);
+       green = red;
+       blue = red;
+    }
+    else {
+      var h = (hue - Math.floor(hue)) * 6.0;
+      var f = h - Math.floor(h);
+      var p = brightness * (1.0 - saturation);
+      var q = brightness * (1.0 - saturation * f);
+      var t = brightness * (1.0 - (saturation * (1.0 - f)));
+
+      switch (parseInt(h)) {
+         case 0:
+            red   = (brightness * 255.0 + 0.5);
+            green = (t * 255.0 + 0.5);
+            blue  = (p * 255.0 + 0.5);
+            break;
+         case 1:
+            red   = (q * 255.0 + 0.5);
+            green = (brightness * 255.0 + 0.5);
+            blue  = (p * 255.0 + 0.5);
+            break;
+         case 2:
+            red   = (p * 255.0 + 0.5);
+            green = (brightness * 255.0 + 0.5);
+            blue  = (t * 255.0 + 0.5);
+            break;
+         case 3:
+            red   = (p * 255.0 + 0.5);
+            green = (q * 255.0 + 0.5);
+            blue  = (brightness * 255.0 + 0.5);
+            break;
+         case 4:
+            red   = (t * 255.0 + 0.5);
+            green = (p * 255.0 + 0.5);
+            blue  = (brightness * 255.0 + 0.5);
+            break;
+          case 5:
+            red   = (brightness * 255.0 + 0.5);
+            green = (p * 255.0 + 0.5);
+            blue  = (q * 255.0 + 0.5);
+            break;
+        }
+    }
+
+   return { r : parseInt(red), g : parseInt(green) , b : parseInt(blue) };
+}
+
+Rico.Color.RGBtoHSB = function(r, g, b) {
+
+   var hue;
+   var saturation;
+   var brightness;
+
+   var cmax = (r > g) ? r : g;
+   if (b > cmax)
+      cmax = b;
+
+   var cmin = (r < g) ? r : g;
+   if (b < cmin)
+      cmin = b;
+
+   brightness = cmax / 255.0;
+   if (cmax != 0)
+      saturation = (cmax - cmin)/cmax;
+   else
+      saturation = 0;
+
+   if (saturation == 0)
+      hue = 0;
+   else {
+      var redc   = (cmax - r)/(cmax - cmin);
+        var greenc = (cmax - g)/(cmax - cmin);
+        var bluec  = (cmax - b)/(cmax - cmin);
+
+        if (r == cmax)
+           hue = bluec - greenc;
+        else if (g == cmax)
+           hue = 2.0 + redc - bluec;
+      else
+           hue = 4.0 + greenc - redc;
+
+        hue = hue / 6.0;
+        if (hue < 0)
+           hue = hue + 1.0;
+   }
+
+   return { h : hue, s : saturation, b : brightness };
+}
+


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/Rico/Color.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/Rico/Corner.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/Rico/Corner.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/Rico/Corner.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,314 @@
+/**  
+*  
+*  Copyright 2005 Sabre Airline Solutions  
+*  
+*  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this  
+*  file except in compliance with the License. You may obtain a copy of the License at  
+*  
+*         http://www.apache.org/licenses/LICENSE-2.0  
+*  
+*  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.  
+**/  
+
+
+var Rico = new Object();
+Rico.Corner = {
+
+   round: function(e, options) {
+      var e = $(e);
+      this._setOptions(options);
+
+      var color = this.options.color;
+      if ( this.options.color == "fromElement" )
+         color = this._background(e);
+
+      var bgColor = this.options.bgColor;
+      if ( this.options.bgColor == "fromParent" )
+         bgColor = this._background(e.offsetParent);
+
+      this._roundCornersImpl(e, color, bgColor);
+   },
+
+    /**   This is a helper function to change the background
+    *     color of <div> that has had Rico rounded corners added.
+    *
+    *     It seems we cannot just set the background color for the
+    *     outer <div> so each <span> element used to create the
+    *     corners must have its background color set individually.
+    *
+    * @param {DOM} theDiv - A child of the outer <div> that was
+    *                        supplied to the `round` method.
+    *
+    * @param {String} newColor - The new background color to use.
+    */
+    changeColor: function(theDiv, newColor) {
+   
+        theDiv.style.backgroundColor = newColor;
+
+        var spanElements = theDiv.parentNode.getElementsByTagName("span");
+        
+        for (var currIdx = 0; currIdx < spanElements.length; currIdx++) {
+            spanElements[currIdx].style.backgroundColor = newColor;
+        }
+    }, 
+
+
+    /**   This is a helper function to change the background
+    *     opacity of <div> that has had Rico rounded corners added.
+    *
+    *     See changeColor (above) for algorithm explanation
+    *
+    * @param {DOM} theDiv A child of the outer <div> that was
+    *                        supplied to the `round` method.
+    *
+    * @param {int} newOpacity The new opacity to use (0-1).
+    */
+    changeOpacity: function(theDiv, newOpacity) {
+   
+        var mozillaOpacity = newOpacity;
+        var ieOpacity = 'alpha(opacity=' + newOpacity * 100 + ')';
+        
+        theDiv.style.opacity = mozillaOpacity;
+        theDiv.style.filter = ieOpacity;
+
+        var spanElements = theDiv.parentNode.getElementsByTagName("span");
+        
+        for (var currIdx = 0; currIdx < spanElements.length; currIdx++) {
+            spanElements[currIdx].style.opacity = mozillaOpacity;
+            spanElements[currIdx].style.filter = ieOpacity;
+        }
+
+    },
+
+    /** this function takes care of redoing the rico cornering
+    *    
+    *    you can't just call updateRicoCorners() again and pass it a 
+    *    new options string. you have to first remove the divs that 
+    *    rico puts on top and below the content div.
+    *
+    * @param {DOM} theDiv - A child of the outer <div> that was
+    *                        supplied to the `round` method.
+    *
+    * @param {Array} options - list of options
+    */
+    reRound: function(theDiv, options) {
+
+        var topRico = theDiv.parentNode.childNodes[0];
+        //theDiv would be theDiv.parentNode.childNodes[1]
+        var bottomRico = theDiv.parentNode.childNodes[2];
+       
+        theDiv.parentNode.removeChild(topRico);
+        theDiv.parentNode.removeChild(bottomRico); 
+
+        this.round(theDiv.parentNode, options);
+    }, 
+
+   _roundCornersImpl: function(e, color, bgColor) {
+      if(this.options.border)
+         this._renderBorder(e,bgColor);
+      if(this._isTopRounded())
+         this._roundTopCorners(e,color,bgColor);
+      if(this._isBottomRounded())
+         this._roundBottomCorners(e,color,bgColor);
+   },
+
+   _renderBorder: function(el,bgColor) {
+      var borderValue = "1px solid " + this._borderColor(bgColor);
+      var borderL = "border-left: "  + borderValue;
+      var borderR = "border-right: " + borderValue;
+      var style   = "style='" + borderL + ";" + borderR +  "'";
+      el.innerHTML = "<div " + style + ">" + el.innerHTML + "</div>"
+   },
+
+   _roundTopCorners: function(el, color, bgColor) {
+      var corner = this._createCorner(bgColor);
+      for(var i=0 ; i < this.options.numSlices ; i++ )
+         corner.appendChild(this._createCornerSlice(color,bgColor,i,"top"));
+      el.style.paddingTop = 0;
+      el.insertBefore(corner,el.firstChild);
+   },
+
+   _roundBottomCorners: function(el, color, bgColor) {
+      var corner = this._createCorner(bgColor);
+      for(var i=(this.options.numSlices-1) ; i >= 0 ; i-- )
+         corner.appendChild(this._createCornerSlice(color,bgColor,i,"bottom"));
+      el.style.paddingBottom = 0;
+      el.appendChild(corner);
+   },
+
+   _createCorner: function(bgColor) {
+      var corner = document.createElement("div");
+      corner.style.backgroundColor = (this._isTransparent() ? "transparent" : bgColor);
+      return corner;
+   },
+
+   _createCornerSlice: function(color,bgColor, n, position) {
+      var slice = document.createElement("span");
+
+      var inStyle = slice.style;
+      inStyle.backgroundColor = color;
+      inStyle.display  = "block";
+      inStyle.height   = "1px";
+      inStyle.overflow = "hidden";
+      inStyle.fontSize = "1px";
+
+      var borderColor = this._borderColor(color,bgColor);
+      if ( this.options.border && n == 0 ) {
+         inStyle.borderTopStyle    = "solid";
+         inStyle.borderTopWidth    = "1px";
+         inStyle.borderLeftWidth   = "0px";
+         inStyle.borderRightWidth  = "0px";
+         inStyle.borderBottomWidth = "0px";
+         inStyle.height            = "0px"; // assumes css compliant box model
+         inStyle.borderColor       = borderColor;
+      }
+      else if(borderColor) {
+         inStyle.borderColor = borderColor;
+         inStyle.borderStyle = "solid";
+         inStyle.borderWidth = "0px 1px";
+      }
+
+      if ( !this.options.compact && (n == (this.options.numSlices-1)) )
+         inStyle.height = "2px";
+
+      this._setMargin(slice, n, position);
+      this._setBorder(slice, n, position);
+      return slice;
+   },
+
+   _setOptions: function(options) {
+      this.options = {
+         corners : "all",
+         color   : "fromElement",
+         bgColor : "fromParent",
+         blend   : true,
+         border  : false,
+         compact : false
+      }
+      OpenLayers.Util.extend(this.options, options || {});
+
+      this.options.numSlices = this.options.compact ? 2 : 4;
+      if ( this._isTransparent() )
+         this.options.blend = false;
+   },
+
+   _whichSideTop: function() {
+      if ( this._hasString(this.options.corners, "all", "top") )
+         return "";
+
+      if ( this.options.corners.indexOf("tl") >= 0 && this.options.corners.indexOf("tr") >= 0 )
+         return "";
+
+      if (this.options.corners.indexOf("tl") >= 0)
+         return "left";
+      else if (this.options.corners.indexOf("tr") >= 0)
+          return "right";
+      return "";
+   },
+
+   _whichSideBottom: function() {
+      if ( this._hasString(this.options.corners, "all", "bottom") )
+         return "";
+
+      if ( this.options.corners.indexOf("bl")>=0 && this.options.corners.indexOf("br")>=0 )
+         return "";
+
+      if(this.options.corners.indexOf("bl") >=0)
+         return "left";
+      else if(this.options.corners.indexOf("br")>=0)
+         return "right";
+      return "";
+   },
+
+   _borderColor : function(color,bgColor) {
+      if ( color == "transparent" )
+         return bgColor;
+      else if ( this.options.border )
+         return this.options.border;
+      else if ( this.options.blend )
+         return this._blend( bgColor, color );
+      else
+         return "";
+   },
+
+
+   _setMargin: function(el, n, corners) {
+      var marginSize = this._marginSize(n);
+      var whichSide = corners == "top" ? this._whichSideTop() : this._whichSideBottom();
+
+      if ( whichSide == "left" ) {
+         el.style.marginLeft = marginSize + "px"; el.style.marginRight = "0px";
+      }
+      else if ( whichSide == "right" ) {
+         el.style.marginRight = marginSize + "px"; el.style.marginLeft  = "0px";
+      }
+      else {
+         el.style.marginLeft = marginSize + "px"; el.style.marginRight = marginSize + "px";
+      }
+   },
+
+   _setBorder: function(el,n,corners) {
+      var borderSize = this._borderSize(n);
+      var whichSide = corners == "top" ? this._whichSideTop() : this._whichSideBottom();
+      if ( whichSide == "left" ) {
+         el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = "0px";
+      }
+      else if ( whichSide == "right" ) {
+         el.style.borderRightWidth = borderSize + "px"; el.style.borderLeftWidth  = "0px";
+      }
+      else {
+         el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = borderSize + "px";
+      }
+      if (this.options.border != false)
+        el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = borderSize + "px";
+   },
+
+   _marginSize: function(n) {
+      if ( this._isTransparent() )
+         return 0;
+
+      var marginSizes          = [ 5, 3, 2, 1 ];
+      var blendedMarginSizes   = [ 3, 2, 1, 0 ];
+      var compactMarginSizes   = [ 2, 1 ];
+      var smBlendedMarginSizes = [ 1, 0 ];
+
+      if ( this.options.compact && this.options.blend )
+         return smBlendedMarginSizes[n];
+      else if ( this.options.compact )
+         return compactMarginSizes[n];
+      else if ( this.options.blend )
+         return blendedMarginSizes[n];
+      else
+         return marginSizes[n];
+   },
+
+   _borderSize: function(n) {
+      var transparentBorderSizes = [ 5, 3, 2, 1 ];
+      var blendedBorderSizes     = [ 2, 1, 1, 1 ];
+      var compactBorderSizes     = [ 1, 0 ];
+      var actualBorderSizes      = [ 0, 2, 0, 0 ];
+
+      if ( this.options.compact && (this.options.blend || this._isTransparent()) )
+         return 1;
+      else if ( this.options.compact )
+         return compactBorderSizes[n];
+      else if ( this.options.blend )
+         return blendedBorderSizes[n];
+      else if ( this.options.border )
+         return actualBorderSizes[n];
+      else if ( this._isTransparent() )
+         return transparentBorderSizes[n];
+      return 0;
+   },
+
+   _hasString: function(str) { for(var i=1 ; i<arguments.length ; i++) if (str.indexOf(arguments[i]) >= 0) return true; return false; },
+   _blend: function(c1, c2) { var cc1 = Rico.Color.createFromHex(c1); cc1.blend(Rico.Color.createFromHex(c2)); return cc1; },
+   _background: function(el) { try { return Rico.Color.createColorFromBackground(el).asHex(); } catch(err) { return "#ffffff"; } },
+   _isTransparent: function() { return this.options.color == "transparent"; },
+   _isTopRounded: function() { return this._hasString(this.options.corners, "all", "top", "tl", "tr"); },
+   _isBottomRounded: function() { return this._hasString(this.options.corners, "all", "bottom", "bl", "br"); },
+   _hasSingleTextChild: function(el) { return el.childNodes.length == 1 && el.childNodes[0].nodeType == 3; }
+}


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/lib/Rico/Corner.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/news.txt
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/news.txt	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/news.txt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,56 @@
+OpenLayers 2.0 Release Notes
+
+ * Deprecated Methods which were removed: 
+
+ For details, see r1228
+
+ * OpenLayers.Map
+   * removed: getFullExtent() -- use  getMaxExtent() instead
+   * removed: zoomToFullExtent() -- use  zoomToMaxExtent() instead
+ * OpenLayers.Layer.Grid
+   * removed: setTileSize() -- user should instead pass a 'tileSize' property 
+              as one of the options when initializing the Grid Layer. ex: 
+        
+        var url = "http://octo.metacarta.com/cgi-bin/mapserv";
+        var options = {tileSize: new OpenLayers.Size(500,50)};
+        var map = new OpenLayers.Map('map', options);
+        layer = new OpenLayers.Layer.WMS(name, url, params);
+ 
+ * OpenLayers.Layer.HTTPRequest
+   * removed: changeParams() -- use mergeNewParams() instead
+ * OpenLayers.Pixel
+   * removed: copyOf() -- use  clone() instead
+ * OpenLayers.Size
+   * removed: copyOf() -- use  clone() instead
+ * OpenLayers.LonLat
+   * removed: copyOf() -- use  clone() instead
+ * OpenLayers.Bounds
+   * removed: copyOf() -- use  clone() instead
+ * Array
+   * removed: copyOf() -- use  clone() instead
+
+ * MouseWheel
+  * It is now possible to zoom in/out with the mouse wheel instead of clicks.
+ 
+ * Spiral Gridding
+  * Instead of loading from the top to bottom of the map, Openlayers will 
+    now start loading tiles from the center of the map.
+
+ * Tile Re-Use
+  * In order to lower memory constraints and reduce element creation times, 
+    OpenLayers now creates a single set of images/tiles and reuses them as 
+    you scroll around the map.
+
+ * New Layer Support
+  * Google 
+  * Virtual Earth
+  * GeoRSS (RSS 1.0 and 2.0 in FF, IE, RSS 2.0 in Safari)
+  * KaMap
+  * Untiled WMS
+
+ * Scale Based Methods 
+  * It is now possible to set zooms based on the concept of scale. See 
+    documentation in the options for the Map constructor.
+
+OpenLayers 1.0
+ Initial release.


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/news.txt
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/readme.txt
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/readme.txt	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/readme.txt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,53 @@
+OpenLayers
+-=-=-=-=-=-
+Copyright (c) 2005-2006 MetaCarta, Inc.
+
+OpenLayers is a JavaScript library for building map applications
+on the web. OpenLayers is made available under a BSD-license.
+Please see license.txt in this distribution for more details.
+
+------------------
+Getting OpenLayers
+------------------
+
+OpenLayers lives at http://www.openlayers.org/.
+
+You can get OpenLayers from
+    http://trac.openlayers.org/wiki/HowToDownload.
+
+---------------------
+Installing OpenLayers
+---------------------
+
+You can use OpenLayers as-is by copying build/OpenLayers.js and the
+entire lib/ directory up to your webserver, putting them in the same
+directory. To include the OpenLayers library in your web page, use:
+
+  <script type="text/javascript" src="OpenLayers.js" />
+
+If you want to use the multiple-file version of OpenLayers (for, say,
+debugging or development purposes), copy the lib/ directory up to your
+webserver in the same directory you put the img/ folder. Then add
+the following to your web page instead:
+
+  <script type="text/javascript" src="lib/OpenLayers.js" />
+
+------------------------------------
+Using OpenLayers in Your Own Website
+------------------------------------
+
+The examples/ directory is full of useful examples.
+
+Dcumentation is available in the doc/ directory: to build an HTML version
+of the documentation, run ./docs.sh in the build/ directory.
+
+Information on changes in the API is available in news.txt.
+
+--------------------------
+Contributing to OpenLayers
+--------------------------
+
+Please join the email lists at http://openlayers.org/EmailLists/.
+Patches are welcome!
+
+= 30 =


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/readme.txt
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/release-license.txt
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/release-license.txt	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/release-license.txt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,29 @@
+This license applies to official code releases at www.openlayers.org:
+
+Copyright (c) 2005-2006 MetaCarta, Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  * Redistributions of source code must retain the above copyright notice, this
+    list of conditions and the following disclaimer.
+
+  * Redistributions in binary form must reproduce the above copyright notice,
+    this list of conditions and the following disclaimer in the documentation
+    and/or other materials provided with the distribution.
+
+  * Neither the name of MetaCarta, Inc. nor the names of its contributors
+    may be used to endorse or promote products derived from this software
+    without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/release-license.txt
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/repository-license.txt
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/repository-license.txt	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/repository-license.txt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,35 @@
+This license applies to all code and content in the OpenLayers code
+repository at svn.openlayers.org:
+
+Copyright (c) 2005-2006 MetaCarta, Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  * Redistributions of source code must retain the above copyright notice, this
+    list of conditions and the following disclaimer.
+
+  * Redistributions in binary form must reproduce the above copyright notice,
+    this list of conditions and the following disclaimer in the documentation
+    and/or other materials provided with the distribution.
+
+  * Neither the name of MetaCarta, Inc. nor the names of its contributors
+    may be used to endorse or promote products derived from this software
+    without specific prior written permission.
+
+This license grants no rights to any components related to natural language
+processing, free text querying, or unstructured information retrieval.  This 
+license grants no rights to components that implement inventions on which
+MetaCarta has patents or has filed applications for patents. 
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/repository-license.txt
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/README.txt
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/README.txt	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/README.txt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,16 @@
+This directory contains unit tests for the OpenLayers library.
+
+Tests use the Test.AnotherWay library from <http://openjsan.org>. The test
+runner is 'run-tests.html' and new test files need to be added to
+'list-tests.html'.
+
+The following file naming conventions are used:
+
+  * A filename that starts with `test_` and has an `.html` extension
+    contains tests. These should contain tests for a specific class.
+
+  * A filename starting with `page_` and has an `.html` extension is a
+    supporting HTML file used in one or more tests.
+
+  * A filename starting with 'data_` is a supporting data file used in one
+    or more tests.


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/README.txt
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/data_Layer_Text_textfile.txt
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/data_Layer_Text_textfile.txt	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/data_Layer_Text_textfile.txt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,3 @@
+point	image
+10,20	http://boston.openguides.org/markers/ORANGE.png
+15,25	http://boston.openguides.org/markers/ORANGE.png


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/data_Layer_Text_textfile.txt
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/georss.txt
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/georss.txt	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/georss.txt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,378 @@
+<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet type="text/css" href="/css/rss.css" ?>
+
+<rdf:RDF  xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+  xmlns="http://purl.org/rss/1.0/"
+  xmlns:dc="http://purl.org/dc/elements/1.1/"
+  xmlns:georss="http://www.georss.org/georss">
+<docs>This is an RSS file.  Copy the URL into your aggregator of choice.  If you don't know what this means and want to learn more, please see: <span>http://platial.typepad.com/news/2006/04/really_simple_t.html</span> for more info.</docs><channel rdf:about="http://platial.com">
+<link>http://platial.com</link>
+<title>Crschmidt's Places At Platial</title>
+<description></description>
+<items>
+<rdf:Seq>
+<rdf:li resource="http://platial.com/place/90306"/>
+<rdf:li resource="http://platial.com/place/67230"/>
+<rdf:li resource="http://platial.com/place/65645"/>
+<rdf:li resource="http://platial.com/place/62200"/>
+<rdf:li resource="http://platial.com/place/28232"/>
+<rdf:li resource="http://platial.com/place/43666"/>
+<rdf:li resource="http://platial.com/place/28394"/>
+<rdf:li resource="http://platial.com/place/28251"/>
+<rdf:li resource="http://platial.com/place/28392"/>
+<rdf:li resource="http://platial.com/place/28391"/>
+<rdf:li resource="http://platial.com/place/28231"/>
+<rdf:li resource="http://platial.com/place/28393"/>
+<rdf:li resource="http://platial.com/place/31685"/>
+<rdf:li resource="http://platial.com/place/28596"/>
+<rdf:li resource="http://platial.com/place/28595"/>
+<rdf:li resource="http://platial.com/place/28594"/>
+<rdf:li resource="http://platial.com/place/28593"/>
+<rdf:li resource="http://platial.com/place/28592"/>
+<rdf:li resource="http://platial.com/place/28591"/>
+<rdf:li resource="http://platial.com/place/28590"/>
+<rdf:li resource="http://platial.com/place/28589"/>
+<rdf:li resource="http://platial.com/place/28588"/>
+<rdf:li resource="http://platial.com/place/28587"/>
+<rdf:li resource="http://platial.com/place/28586"/>
+<rdf:li resource="http://platial.com/place/28585"/>
+<rdf:li resource="http://platial.com/place/28584"/>
+<rdf:li resource="http://platial.com/place/28583"/>
+<rdf:li resource="http://platial.com/place/28582"/>
+<rdf:li resource="http://platial.com/place/28581"/>
+<rdf:li resource="http://platial.com/place/28580"/>
+<rdf:li resource="http://platial.com/place/28579"/>
+<rdf:li resource="http://platial.com/place/28578"/>
+<rdf:li resource="http://platial.com/place/28577"/>
+<rdf:li resource="http://platial.com/place/28576"/>
+<rdf:li resource="http://platial.com/place/28575"/>
+<rdf:li resource="http://platial.com/place/28574"/>
+<rdf:li resource="http://platial.com/place/28573"/>
+<rdf:li resource="http://platial.com/place/28572"/>
+<rdf:li resource="http://platial.com/place/28571"/>
+<rdf:li resource="http://platial.com/place/28570"/>
+</rdf:Seq>
+</items>
+</channel>
+<item rdf:about="http://platial.com/place/90306">
+<link>http://platial.com/place/90306</link>
+<title>Knitting Room</title>
+<description><![CDATA[This little shop is jammed full. Yarn, yarn everywhere. They make the most of every possible nook and cranny. I like this place also because they have a lot of different kinds of knitting needles in all different sizes. Also, the people who work here are younger and hipper than in the other stores I go to. I reccomend buying supplies here and then knitting your way through a good documentary at the Capitol Theater across the street.<br/>Address: 2 lake St, Arlington, MA <br/>Tags: knitting, yarn, pins and needles, handspun, hand dyed, novelty yarn, fancy, simple, young, hip, friendly, needles, addy, cute hats<br /><br /><a href="http://platial.com/place/90306">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/90306">Grab this on Platial</a> ]]></description>
+<georss:point>42.405696 -71.142197</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-06-08T17:35:01.942452+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/67230">
+<link>http://platial.com/place/67230</link>
+<title>Knitting Room</title>
+<description><![CDATA[This little shop is jammed full. Yarn, yarn everywhere. They make the most of every possible nook and cranny. I like this place also because they have a lot of different kinds of knitting needles in all different sizes. Also, the people who work here are younger and hipper than in the other stores I go to. I reccomend buying supplies here and then knitting your way through a good documentary at the Capitol Theater across the street.<br/>Address: 2 lake St, Arlington, MA <br/>Tags: knitting, yarn, pins and needles, handspun, hand dyed, novelty yarn, fancy, simple, young, hip, friendly, needles, addy, cute hats<br /><br /><a href="http://platial.com/place/67230">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/67230">Grab this on Platial</a> ]]></description>
+<georss:point>42.405524 -71.142273</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-04-24T11:35:26.733857+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/65645">
+<link>http://platial.com/place/65645</link>
+<title>†¢¢™£ˆøœ</title>
+<description><![CDATA[ijeª£∆µˆ˚î<br/>Address: 151 Erie St., Cambridge, MA<br/>Tags: platial graffiti<br /><br /><a href="http://platial.com/place/65645">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/65645">Grab this on Platial</a> ]]></description>
+<georss:point>42.352455 -71.110210</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-04-20T08:56:12.696224+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/62200">
+<link>http://platial.com/place/62200</link>
+<title>Allen Hall</title>
+<description><![CDATA[My dorm at UIUC.<br/>Address: 1301 W Gregory Dr, Urbana, IL<br/>Tags: dorm, uiuc, college<br/><a href="http://platial.com/place/62200"><img src="http://platial.comhttp://static.flickr.com/4/8576450_0d59cc2531_s.jpg"/></a><br/><br /><br /><a href="http://platial.com/place/62200">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/62200">Grab this on Platial</a> ]]></description>
+<georss:point>40.104172 -88.220623</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-04-14T08:01:01.872873+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28232">
+<link>http://platial.com/place/28232</link>
+<title>Bagby Hot Springs, OR</title>
+<description><![CDATA[Hot spring, temperature: 136  degress F, 58  degress C. However, the area around the springs are not exactly well looked upon by people who know the place.
+
+<br/>Tags: 20s, rosalie, romance, childhood, hike, camping, soak, relax, beautiful, hot springs, bathhouse, favorite, popular, crowded, organized, honeymoon tub, plumbing made from hollowed out trees, hot springs, mt hood, notorious car break in spot, rash, bacteria<br /><br /><a href="http://platial.com/place/28232">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28232">Grab this on Platial</a> ]]></description>
+<georss:point>44.936000 -122.173000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:10:18.553063+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/43666">
+<link>http://platial.com/place/43666</link>
+<title>Shooting Location for "The Field of Dreams" Film</title>
+<description><![CDATA[1989's Field of Dreams was a Best Picture Academy Award nominee, and the baseball field in the cornfield still stands today, and has become quite a tourist destination.  Games are occasionally played at the field, re-enacting professional baseball at the turn of the 20th Century.<br/>Address: Dyersville, Iowa<br/>Tags: iowa, baseball, movie locations, field of dreams, kevin costner, costner, dyersville, kinsella, james earl jones, chicago black sox, shoeless joe, joe jackson, famous farms, film, movie, cinema, shooting location<br /><br /><a href="http://platial.com/place/43666">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/43666">Grab this on Platial</a> ]]></description>
+<georss:point>42.481213 -91.111679</georss:point>
+<dc:creator>echinodermata</dc:creator>
+<dc:date>2006-03-23T11:40:17.654061+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28394">
+<link>http://platial.com/place/28394</link>
+<title>Moffetts (Bonneville) Hot Springs, WA</title>
+<description><![CDATA[Hot spring, temperature: 97  degress F, 36  degress C<br/>Tags: soak, hot springs, relax, nature<br /><br /><a href="http://platial.com/place/28394">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28394">Grab this on Platial</a> ]]></description>
+<georss:point>45.658000 -121.962000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:16:27.329816+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28251">
+<link>http://platial.com/place/28251</link>
+<title>Austin Hot Springs, OR</title>
+<description><![CDATA[Hot spring, temperature: 186  degress F, 86  degress C<br/>Tags: soak, hot springs, relax, nature, popular, crowded<br /><br /><a href="http://platial.com/place/28251">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28251">Grab this on Platial</a> ]]></description>
+<georss:point>45.021000 -122.009000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:11:04.489886+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28392">
+<link>http://platial.com/place/28392</link>
+<title>Rock Creek Hot Springs, WA</title>
+<description><![CDATA[Hot spring, temperature: Hot  degress F, Hot  degress C<br/>Tags: soak, hot springs, relax, nature<br /><br /><a href="http://platial.com/place/28392">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28392">Grab this on Platial</a> ]]></description>
+<georss:point>45.723000 -121.927000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:16:22.636855+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28391">
+<link>http://platial.com/place/28391</link>
+<title>St. Martins (Wind River) Hot Springs, WA</title>
+<description><![CDATA[Hot spring, temperature: 120  degress F, 49  degress C<br/>Tags: hot springs, soak, relax, nature, wonderful<br /><br /><a href="http://platial.com/place/28391">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28391">Grab this on Platial</a> ]]></description>
+<georss:point>45.728000 -121.800000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:16:20.383244+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28231">
+<link>http://platial.com/place/28231</link>
+<title>Breitenbush Hot Springs, OR</title>
+<description><![CDATA[Hot spring, temperature: 198  degress F, 92  degress C<br/>Tags: hot springs, resort, relax, nature, beautiful, http:www.breitenbush.com, soaking<br /><br /><a href="http://platial.com/place/28231">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28231">Grab this on Platial</a> ]]></description>
+<georss:point>44.782000 -121.975000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:10:16.529195+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28393">
+<link>http://platial.com/place/28393</link>
+<title>Collins Hot Springs, WA</title>
+<description><![CDATA[Hot spring, temperature: 122  degress F, 50  degress C<br/>Tags: portland, nice, hot springs, soak<br /><br /><a href="http://platial.com/place/28393">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28393">Grab this on Platial</a> ]]></description>
+<georss:point>45.701000 -121.728000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:16:24.648745+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/31685">
+<link>http://platial.com/place/31685</link>
+<title>Darwin's Ltd.</title>
+<description><![CDATA[Nice little coffee shop/cafe, free Wifi, close enough to walk from Harvard Square.<br/>Address: 148 Mount Auburn St, Cambridge, MA<br/>Tags: coffee, beer, sandwiches, freewifi<br/><a href="http://platial.com/place/31685"><img src="http://platial.comhttp://static.flickr.com/38/84885937_74fd3d1025_s.jpg"/></a><br/><br /><br /><a href="http://platial.com/place/31685">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/31685">Grab this on Platial</a> ]]></description>
+<georss:point>42.373974 -71.125053</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-10T09:24:08.152985+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28596">
+<link>http://platial.com/place/28596</link>
+<title>Huckleberry Hot Springs, WY</title>
+<description><![CDATA[Hot spring, temperature: Boiling  degress F, Boiling  degress C<br /><br /><a href="http://platial.com/place/28596">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28596">Grab this on Platial</a> ]]></description>
+<georss:point>44.115000 -110.684000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:24:32.283094+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28595">
+<link>http://platial.com/place/28595</link>
+<title>South Entrance Hot Springs, WY</title>
+<description><![CDATA[Hot spring, temperature: 156  degress F, 69  degress C<br/><a href="http://platial.com/place/28595"><img src="http://platial.comhttp://static.flickr.com/52/130989872_f1457f68b5_s.jpg"/></a><br/><br /><br /><a href="http://platial.com/place/28595">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28595">Grab this on Platial</a> ]]></description>
+<georss:point>44.142000 -110.656000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:24:30.279497+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28594">
+<link>http://platial.com/place/28594</link>
+<title>Crawfish Creek Hot Springs, WY</title>
+<description><![CDATA[Hot spring, temperature: 136  degress F, 58  degress C<br/><a href="http://platial.com/place/28594"><img src="http://platial.comhttp://static.flickr.com/52/128312256_d6a879924c_s.jpg"/></a><br/><br /><br /><a href="http://platial.com/place/28594">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28594">Grab this on Platial</a> ]]></description>
+<georss:point>44.157000 -110.699000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:24:28.280271+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28593">
+<link>http://platial.com/place/28593</link>
+<title>Crawfish Creek Hot Springs, WY</title>
+<description><![CDATA[Hot spring, temperature: 138  degress F, 59  degress C<br /><br /><a href="http://platial.com/place/28593">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28593">Grab this on Platial</a> ]]></description>
+<georss:point>44.165000 -110.723000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:24:20.364077+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28592">
+<link>http://platial.com/place/28592</link>
+<title>Snake Hot Springs, WY</title>
+<description><![CDATA[Hot spring, temperature: 136  degress F, 58  degress C<br /><br /><a href="http://platial.com/place/28592">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28592">Grab this on Platial</a> ]]></description>
+<georss:point>44.169000 -110.583000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:24:12.234974+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28591">
+<link>http://platial.com/place/28591</link>
+<title>Hot Springs, WY</title>
+<description><![CDATA[Hot spring, temperature: 142  degress F, 61  degress C<br /><br /><a href="http://platial.com/place/28591">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28591">Grab this on Platial</a> ]]></description>
+<georss:point>44.187000 -110.726000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:24:10.027857+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28590">
+<link>http://platial.com/place/28590</link>
+<title>Hot Springs on Upper Snake River, WY</title>
+<description><![CDATA[Hot spring, temperature: 167  degress F, 75  degress C<br /><br /><a href="http://platial.com/place/28590">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28590">Grab this on Platial</a> ]]></description>
+<georss:point>44.204000 -110.486000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:24:07.79658+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28589">
+<link>http://platial.com/place/28589</link>
+<title>Hot Springs on lewis Lake, WY</title>
+<description><![CDATA[Hot spring, temperature: 154  degress F, 68  degress C<br /><br /><a href="http://platial.com/place/28589">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28589">Grab this on Platial</a> ]]></description>
+<georss:point>44.276000 -110.636000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:24:05.683418+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28588">
+<link>http://platial.com/place/28588</link>
+<title>Rustic Geyser, WY</title>
+<description><![CDATA[Hot spring, temperature: 199  degress F, 93  degress C<br /><br /><a href="http://platial.com/place/28588">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28588">Grab this on Platial</a> ]]></description>
+<georss:point>44.282000 -110.506000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:24:03.66329+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28587">
+<link>http://platial.com/place/28587</link>
+<title>Bechler River Hot Springs, WY</title>
+<description><![CDATA[Hot spring, temperature: 194  degress F, 90  degress C<br /><br /><a href="http://platial.com/place/28587">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28587">Grab this on Platial</a> ]]></description>
+<georss:point>44.285000 -110.900000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:24:01.611442+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28586">
+<link>http://platial.com/place/28586</link>
+<title>Hot Springs, WY</title>
+<description><![CDATA[Hot spring, temperature: Boiling  degress F, 201  degress C<br /><br /><a href="http://platial.com/place/28586">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28586">Grab this on Platial</a> ]]></description>
+<georss:point>44.290000 -110.504000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:59.658699+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28585">
+<link>http://platial.com/place/28585</link>
+<title>Heart Lake Geyser Basin, WY</title>
+<description><![CDATA[Hot spring, temperature:  Middle Group  degress F, 174  degress C<br /><br /><a href="http://platial.com/place/28585">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28585">Grab this on Platial</a> ]]></description>
+<georss:point>44.299000 -110.517000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:57.181801+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28584">
+<link>http://platial.com/place/28584</link>
+<title>Hot Springs, WY</title>
+<description><![CDATA[Hot spring, temperature: Boiling  degress F, 201  degress C<br /><br /><a href="http://platial.com/place/28584">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28584">Grab this on Platial</a> ]]></description>
+<georss:point>44.307000 -110.526000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:55.240485+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28583">
+<link>http://platial.com/place/28583</link>
+<title>Hot Springs on lewis Lake, WY</title>
+<description><![CDATA[Hot spring, temperature: 199  degress F, 93  degress C<br /><br /><a href="http://platial.com/place/28583">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28583">Grab this on Platial</a> ]]></description>
+<georss:point>44.309000 -110.654000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:53.22295+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28582">
+<link>http://platial.com/place/28582</link>
+<title>Shoshone Geyser Basin, WY</title>
+<description><![CDATA[Hot spring, temperature: 203  degress F, 95  degress C<br /><br /><a href="http://platial.com/place/28582">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28582">Grab this on Platial</a> ]]></description>
+<georss:point>44.354000 -110.800000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:51.179049+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28581">
+<link>http://platial.com/place/28581</link>
+<title>Hot Springs on Continental Divide, WY</title>
+<description><![CDATA[Hot spring, temperature: 189  degress F, 87  degress C<br /><br /><a href="http://platial.com/place/28581">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28581">Grab this on Platial</a> ]]></description>
+<georss:point>44.401000 -110.936000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:49.077176+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28580">
+<link>http://platial.com/place/28580</link>
+<title>Hot Springs on Upper Firehole River, WY</title>
+<description><![CDATA[Hot spring, temperature: Hot  degress F, Hot  degress C<br /><br /><a href="http://platial.com/place/28580">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28580">Grab this on Platial</a> ]]></description>
+<georss:point>44.404000 -110.824000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:47.054664+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28579">
+<link>http://platial.com/place/28579</link>
+<title>Summit Lake Hot Springs, WY</title>
+<description><![CDATA[Hot spring, temperature: 162  degress F, 72  degress C<br /><br /><a href="http://platial.com/place/28579">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28579">Grab this on Platial</a> ]]></description>
+<georss:point>44.410000 -110.953000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:45.039394+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28578">
+<link>http://platial.com/place/28578</link>
+<title>Lone Star Geyser Basin, WY</title>
+<description><![CDATA[Hot spring, temperature:  Footbridge  degress F, 183  degress C<br /><br /><a href="http://platial.com/place/28578">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28578">Grab this on Platial</a> ]]></description>
+<georss:point>44.414000 -110.817000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:42.938808+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28577">
+<link>http://platial.com/place/28577</link>
+<title>West. Thumb Geyser Basin, WY</title>
+<description><![CDATA[Hot spring, temperature: 203  degress F, 95  degress C<br /><br /><a href="http://platial.com/place/28577">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28577">Grab this on Platial</a> ]]></description>
+<georss:point>44.417000 -110.570000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:40.90238+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28576">
+<link>http://platial.com/place/28576</link>
+<title>Lone Star Geyser, WY</title>
+<description><![CDATA[Hot spring, temperature: 199  degress F, 93  degress C<br /><br /><a href="http://platial.com/place/28576">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28576">Grab this on Platial</a> ]]></description>
+<georss:point>44.418000 -110.805000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:38.844625+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28575">
+<link>http://platial.com/place/28575</link>
+<title>Smoke Jumper Hot Springs, WY</title>
+<description><![CDATA[Hot spring, temperature: 198  degress F, 92  degress C<br /><br /><a href="http://platial.com/place/28575">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28575">Grab this on Platial</a> ]]></description>
+<georss:point>44.421000 -110.952000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:36.818513+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28574">
+<link>http://platial.com/place/28574</link>
+<title>West. Thumb Geyser Basin, WY</title>
+<description><![CDATA[Hot spring, temperature: 196  degress F, 91  degress C<br /><br /><a href="http://platial.com/place/28574">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28574">Grab this on Platial</a> ]]></description>
+<georss:point>44.422000 -110.574000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:34.767729+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28573">
+<link>http://platial.com/place/28573</link>
+<title>Potts Hot Spring Basin, WY</title>
+<description><![CDATA[Hot spring, temperature: 203  degress F, 95  degress C<br /><br /><a href="http://platial.com/place/28573">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28573">Grab this on Platial</a> ]]></description>
+<georss:point>44.433000 -110.581000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:32.749915+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28572">
+<link>http://platial.com/place/28572</link>
+<title>Hot Springs, WY</title>
+<description><![CDATA[Hot spring, temperature: Hot  degress F, Hot  degress C<br /><br /><a href="http://platial.com/place/28572">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28572">Grab this on Platial</a> ]]></description>
+<georss:point>44.433000 -110.813000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:30.829745+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28571">
+<link>http://platial.com/place/28571</link>
+<title>Hot Springs on Continental Divide, WY</title>
+<description><![CDATA[Hot spring, temperature: Hot  degress F, Hot  degress C<br /><br /><a href="http://platial.com/place/28571">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28571">Grab this on Platial</a> ]]></description>
+<georss:point>44.438000 -110.977000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:28.730401+00:00</dc:date>
+</item>
+<item rdf:about="http://platial.com/place/28570">
+<link>http://platial.com/place/28570</link>
+<title>SouthEastern Group, WY</title>
+<description><![CDATA[Hot spring, temperature: 198  degress F, 92  degress C<br /><br /><a href="http://platial.com/place/28570">Map this on Platial</a><br /> <a href="http://platial.com/place_grab/28570">Grab this on Platial</a> ]]></description>
+<georss:point>44.459000 -110.817000</georss:point>
+<dc:creator>crschmidt</dc:creator>
+<dc:date>2006-01-03T23:23:26.706763+00:00</dc:date>
+</item>
+</rdf:RDF>
\ No newline at end of file


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/georss.txt
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/list-tests.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/list-tests.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/list-tests.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,34 @@
+<ul id="testlist">
+    <li>test_Class.html</li>
+    <li>test_Pixel.html</li>
+    <li>test_Size.html</li>
+    <li>test_LonLat.html</li>
+    <li>test_Icon.html</li>
+    <li>test_Marker.html</li>
+    <li>test_Popup.html</li>
+    <li>test_Feature.html</li>
+    <li>test_Bounds.html</li>
+    <li>test_Events.html</li>
+    <li>test_Util.html</li>
+    <li>test_Layer.html</li>
+    <li>test_Layer_Image.html</li>
+    <li>test_Layer_EventPane.html</li>
+    <li>test_Layer_FixedZoomLevels.html</li>
+    <li>test_Layer_HTTPRequest.html</li>
+    <li>test_Layer_Grid.html</li>
+    <li>test_Layer_Markers.html</li>
+    <li>test_Layer_Text.html</li>
+    <li>test_Layer_GeoRSS.html</li>
+    <li>test_Layer_KaMap.html</li>
+    <li>test_Layer_WMS.html</li>
+    <li>test_Tile.html</li>
+    <li>test_Tile_Image.html</li>
+    <li>test_Control.html</li>
+    <li>test_Control_OverviewMap.html</li>
+    <li>test_Control_MouseToolbar.html</li>
+    <li>test_Control_LayerSwitcher.html</li>
+    <li>test_Control_PanZoom.html</li>
+    <li>test_Control_PanZoomBar.html</li>
+    <li>test_Control_Permalink.html</li>
+    <li>test_Map.html</li>
+</ul>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/list-tests.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/run-tests.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/run-tests.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/run-tests.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,2384 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<html><head><title> Run the testsuite</title>
+<noscript>Javascript is disabled in your browser. This page cannot be displayed correctly without Javascript. Sorry. <br/> If you want to view this page, please change your browser settings so that Javascript is enabled.</noscript>
+<!--
+Test.AnotherWay version 0.5
+
+Copyright (c) 2005 Artem Khodush, http://straytree.org
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+-->
+<style type="text/css">
+* { padding: 0; margin: 0; }
+html { height: 99%; }
+body { height: 98%; font: normal normal 10pt sans-serif }
+#col1 {  float: left; width: 27em; margin: 0 0 0 1em; overflow: visible; }
+#col2 {  position: relative; height: 98%; margin: 0 0.5em 0 28em; }
+#col1_header { margin-top: 0.5em; }
+#scroller { height: 550px; overflow: auto;}
+#testtable { margin: 0 0 2em 0; width: 97%; }
+#run_buttons { margin-bottom: 4em; }
+
+#right_header { padding-top: 0.8em; }
+#results_count { float: left; }
+.active_tab 			{ float: right; padding: 0 1em 0.2em 1em; background: #0af; border: 1px solid #048; border-bottom: none; cursor: pointer; cursor: hand;
+					 position: relative; top: -0.2em; }
+.inactive_tab 			{ float: right; padding: 0 1em 0 1em; background: #9bb; color: #444; border: 1px solid #9bb; border-bottom: none; cursor: pointer; cursor: hand; }
+.inactive_mouseover_tab 	{ float: right; padding: 0 1em 0 1em; background: #9bb; color: #062; border: 1px solid #062; border-bottom: none; cursor: pointer; cursor: hand; }
+
+#right_frame { overflow: auto; position: relative; top: -0.2em; clear: right; height: 95%; border: 1px solid #048; }
+
+#debug { display: none; }
+#debug p { margin: 2px 0 0 5em; text-indent: -4.8em; }
+
+#error { display: none; color: #c22; }
+
+#results p { margin: 0 0 2px 0; }
+/* cursor indicating that detailed results may be expanded/contracted */
+#results p.badtest { cursor: text; }
+#results p.ok, #results p.fail { cursor: pointer; cursor: hand; }
+
+/* colored squares in the results window at the left of test page names */
+#results p.ok .bullet { background: #6d6; }
+#results p.fail .bullet { background:  #d46; }
+#results p.badtest .bullet { background: #ea3; }
+#results p.loading .bullet { background: #48f; }
+#results p.running .bullet { background: #26e; }
+#results p.waiting .bullet { background: #04d; }
+/* highlight in the results line */
+#results p .warning { background: #ffc; }
+
+/* layout of the detailed results */
+.result_detail { padding-left: 3em; }
+.result_exception_detail { padding-left: 4em; }
+.result_exception_stack_detail { padding-left: 5em;  }
+.result_micro_detail { padding-left: 6em; }
+/* colouring in the detailed results */
+.result_detail .fail, .result_exception_detail .fail,  .result_micro_detail .fail { background: #ffd8d8; }
+
+/* "start recording" controls*/
+#record_div { margin-top: 3em; }
+#record_div p { margin-bottom: 0.5em; }
+#record_select { width: 88%; }
+#record_input { width: 53%; }
+</style>
+<script type="text/javascript">
+<!--
+if( typeof( Test )=="undefined" ) {
+	Test={};
+}
+Test.AnotherWay={};
+
+Test.AnotherWay._g_test_iframe=null; // frame where to load test pages
+Test.AnotherWay._g_test_frame_no_clear=false; // true - leave last page displayed after tests end
+Test.AnotherWay._g_test_page_urls=[]; // array of: { url: url, convention: "anotherway" or "jsan" }
+Test.AnotherWay._g_test_object_for_jsan=null; // test object for filling by tests that adhere to jsan Test.Simple calling convention
+Test.AnotherWay._g_pages_to_run=null; // list of pages to run automatically after loading
+Test.AnotherWay._g_run_on_main_load=false; // special handling for run_pages_to_run when it might be called before onload or before list of test pages is known.
+Test.AnotherWay._g_run_on_list_load=false;
+Test.AnotherWay._g_main_loaded=false;
+
+Test.AnotherWay._run_pages_to_run=function( called_from_outside )
+{
+	if( !Test.AnotherWay._g_main_loaded ) {
+		Test.AnotherWay._g_run_on_main_load=true;
+	}else {
+		var a_pages=Test.AnotherWay._g_pages_to_run;
+		if( a_pages=="all" ) {
+			for( var i=0; i<Test.AnotherWay._g_test_page_urls.length; ++i ) {
+				Test.AnotherWay._run_test_page( "test"+i );
+			}
+		}else if( a_pages!=null ) {
+			for( var run_i=0; run_i<a_pages.length; ++run_i ) {
+				var run_page=a_pages[run_i];
+				var found=false;
+				for( var all_i=0; all_i<Test.AnotherWay._g_test_page_urls.length; ++all_i ) {
+					if( run_page==Test.AnotherWay._g_test_page_urls[all_i].url ) {
+						Test.AnotherWay._run_test_page( "test"+all_i, called_from_outside );
+						found=true;
+						break;
+					}
+				}
+				if( !found ) {
+					Test.AnotherWay._show_error( "page specified to run is not found in the page list: "+run_page );
+					break;
+				}
+			}
+		}
+	}
+}
+
+Test.AnotherWay._add_test_page_url=function( test_url, convention )
+{
+	var table=document.getElementById( "testtable" );
+	var record_select=document.getElementById( "record_select" );
+	var index=Test.AnotherWay._g_test_page_urls.length;
+
+	// trim spaces.
+	if( test_url.match( "^(\\s*)(.*\\S)(\\s*)$" ) ) {
+		test_url=RegExp.$2;
+	}
+
+	Test.AnotherWay._g_test_page_urls[index]={ url: test_url, convention: convention };
+	var row=table.insertRow( -1 );
+
+	var cell;
+	var cell_child;
+	cell=row.insertCell( -1 );
+	cell_child=document.createElement( "input" );
+	cell_child.type="checkbox";
+	cell_child.id="checkbox"+index;
+	cell.appendChild( cell_child );
+
+	cell=row.insertCell( -1 );
+	cell.setAttribute( "width", "75%" );
+	cell.appendChild( document.createTextNode( test_url ) );
+
+	cell=row.insertCell( -1 );
+	cell_child=document.createElement( "input" );
+	cell_child.type="button";
+	cell_child.id="test"+index;
+	cell_child.value=" run ";
+	cell_child.onclick=Test.AnotherWay._run_one_onclick;
+	cell.appendChild( cell_child );
+
+	cell=row.insertCell( -1 );
+	cell.setAttribute( "width", "8em" );
+	cell_child=document.createElement( "span" );
+	cell.appendChild( cell_child );
+
+	var option=document.createElement( "option" );
+	option.appendChild( document.createTextNode( test_url ) );
+	record_select.appendChild( option );
+}
+Test.AnotherWay._show_error=function( msg )
+{
+	var error_div=document.getElementById( "error" );
+	error_div.innerHTML="";
+	error_div.appendChild( document.createTextNode( msg ) );
+	error_div.style.display="block";
+}
+
+// read urls from the list in the html file inside the list_iframe
+// fill on-screen list with urls and "run" buttons, and fill the g_test_page_urls object.
+Test.AnotherWay._list_iframe_onload=function()
+{
+	if( window.frames.list_iframe!=null && window.frames.list_iframe.location!="" && window.frames.list_iframe.location!="about:blank" ) {
+		var list_doc=window.frames.list_iframe.document;
+		var list=list_doc.getElementById( "testlist" );
+		if( list!=null ) {
+			for( var i=0; i<list.childNodes.length; ++i ) {
+				var item=list.childNodes[i];
+				if( item.nodeName=="LI" || item.nodeName=="li" ) {
+					var convention="anotherway";
+					if( Test.AnotherWay._get_css_class( item )=="jsan" ) {
+						convention="jsan";
+					}
+					Test.AnotherWay._add_test_page_url( item.innerHTML, convention );
+				}
+			}
+			if( Test.AnotherWay._g_run_on_list_load ) {
+				Test.AnotherWay._g_run_on_list_load=false;
+				Test.AnotherWay._run_pages_to_run();
+			}
+		}else {
+			Test.AnotherWay._show_error( "no list with id 'testlist' in a list file "+window.frames.list_iframe.location );
+		}
+	}
+}
+
+Test.AnotherWay._map_checkboxes=function( f )
+{
+	var table=document.getElementById( "testtable" );
+	var checks=table.getElementsByTagName( "INPUT" );
+	for( var i=0; i<checks.length; ++i ) {
+		if( checks[i].type=="checkbox" && checks[i].id.match( /^checkbox(\d+)$/ ) ) {
+			f( checks[i], RegExp.$1 );
+		}
+	}
+}
+Test.AnotherWay._run_all_onclick=function()
+{
+	Test.AnotherWay._map_checkboxes( function( c, id ) { Test.AnotherWay._run_test_page( "test"+id ); } );
+}
+Test.AnotherWay._run_selected_onclick=function()
+{
+	Test.AnotherWay._map_checkboxes( function( c, id ) { if( c.checked ) Test.AnotherWay._run_test_page( "test"+id ); } );
+}
+Test.AnotherWay._unselect_all_onclick=function()
+{
+	Test.AnotherWay._map_checkboxes( function( c, id ) { c.checked=false; } );
+}
+Test.AnotherWay._run_one_onclick=function()
+{
+	Test.AnotherWay._run_test_page( this.id );
+}
+
+// construct an object that will gather results of running one test function
+Test.AnotherWay._test_object_t=function( fun_name )
+{
+	this.name=fun_name; // name of the test function
+	this.n_plan=null; // planned number of assertions
+	this.n_ok=0; // # of ok assertions
+	this.n_fail=0; // # of failed assertions
+	this.exception=""; // if the function throwed an exception, it's its message
+	this.exception_stack=[]; // strings: function call stack from the exception
+	this.assertions=[]; // assertion results: array of { ok: 1 or 0, name: string }
+	this.wait_result_milliseconds=0; // how long to wait before collecting results from the test
+	this.second_wait_msg=null; // <p> status message (in addition to the page wait_msg)
+	this.delay_actions=[]; // array of actions to be perfomed after the test function returns
+				//	action : { acton_kind: "call" | "window" | "replay"
+				//				when "call": 		{ call_fn call_delay_milliseconds } call_fn takes nothing
+				//				when "window" : 	{ wnd_url wnd_wnd wnd_fn wnd_timeout_milliseconds wnd_dont_close } wnd_fn takes wnd
+				//				wnen "replay" : 	{ replay_wnd replay_events replay_event_i replay_checkpoints } checkpoint_fn takes this, wnd
+				//	}
+	this.delay_action_i=null; // index of delay action currently being performed
+	this.delay_prev_timer_time=0;	// for counting time while performing delay_actions
+	this.delay_current_milliseconds_left=0; // time left before the next action, runs down
+	this.delay_total_milliseconds_left=0; 	// for indication: total estimated time for all actions, runs up and down
+}
+
+Test.AnotherWay._test_object_t.prototype.ok=function( cond, name )
+{
+	if( cond ) {
+		++this.n_ok;
+		cond=1;
+	}else {
+		++this.n_fail;
+		cond=0;
+	}
+	this.assertions.push( { ok: cond, name: name } );
+}
+Test.AnotherWay._test_object_t.prototype.fail=function( name )
+{
+	this.ok( false, name );
+}
+Test.AnotherWay._test_object_t.prototype.plan=function( n )
+{
+	this.n_plan=n;
+}
+Test.AnotherWay._test_object_t.prototype.wait_result=function( seconds )
+{
+	this.wait_result_milliseconds=1000*seconds;
+}
+Test.AnotherWay._eq_fail_msg=function( path, what, expected, got )
+{
+	return "eq: "+path+" "+what+" differ: got "+got+", but expected "+expected;
+}
+Test.AnotherWay._array_eq=function( expected, got, path, msg )
+{
+	if( expected.length!=got.length ) {
+		msg.msg=Test.AnotherWay._eq_fail_msg( path, "array length", expected.length, got.length );
+		return false;
+	}
+	for( var i=0; i<expected.length; ++i ) {
+		if( !Test.AnotherWay._thing_eq( expected[i], got[i], path+"["+i+"]", msg ) ) {
+			return false;
+		}
+	}
+	return true;
+}
+Test.AnotherWay._object_eq=function( expected, got, path, msg )
+{
+	var v;
+	for( v in expected ) {
+		if( ! (v in got) ) {
+			msg.msg=Test.AnotherWay._eq_fail_msg( path+"."+v, "properties", expected[v], "undefined" );
+			return false;
+		}
+		if( !Test.AnotherWay._thing_eq( expected[v], got[v], path+"."+v, msg ) ) {
+			return false;
+		}
+	}
+	for( v in got ) {
+		if( ! (v in expected) ) {
+			msg.msg=Test.AnotherWay._eq_fail_msg( path+"."+v, "properties", "undefined", got[v] );
+			return false;
+		}
+	}
+	return true;
+}
+Test.AnotherWay._constructor_name=function( x )
+{
+	if( x==null ) {
+		return "";
+	}
+	var s="unknown";
+	try {
+		s=typeof( x.constructor );
+		if( s!="unknown" ) {
+			s=x.constructor.toString();
+		}
+	}catch( e ) {
+		s="unknown";
+	}
+	if( s=="unknown" ) {
+		// hackish attempt to guess a type
+		var is_array=true;
+		var index=0;
+		for( i in x ) {
+			if( i!=index ) {
+				is_array=false;
+			}
+			++index;
+		}
+		return is_array ? "Array" : "Object"; // for empty arrays/objects, this will be wrong half the time
+	}else if( s.match( /^\s*function\s+(\w+)\s*\(/ ) ) {
+		return RegExp.$1;
+	}else {
+		return "";
+	}
+}
+Test.AnotherWay._is_array=function( x )
+{
+	return Test.AnotherWay._constructor_name( x )=="Array";
+}
+Test.AnotherWay._is_value_type=function( x )
+{
+	cn=Test.AnotherWay._constructor_name( x );
+	return cn=="Number" || cn=="String" || cn=="Boolean" || cn=="Date";
+}
+Test.AnotherWay._thing_eq=function( expected, got, path, msg )
+{
+	if( expected==null && got==null ) {
+		return true;
+	}else if( (expected==null && got!=null) || (expected!=null && got==null) ) {
+		msg.msg=Test.AnotherWay._eq_fail_msg( path, "values", expected, got );
+		return false;
+	}else {
+		var expected_cn=Test.AnotherWay._constructor_name( expected );
+		var got_cn=Test.AnotherWay._constructor_name( got );
+		if( expected_cn!=got_cn ) {
+			msg.msg=Test.AnotherWay._eq_fail_msg( path, "types", expected_cn, got_cn );
+			return false;
+		}else {
+			if( Test.AnotherWay._is_array( expected ) ) {
+				return Test.AnotherWay._array_eq( expected, got, path, msg );
+			}else if( Test.AnotherWay._is_value_type( expected ) ) {
+				if( expected!=got ) {
+					msg.msg=Test.AnotherWay._eq_fail_msg( path, "values", expected, got );
+					return false;
+				}else {
+					return true;
+				}
+			}else { // just a plain object
+				return Test.AnotherWay._object_eq( expected, got, path, msg );
+			}
+		}
+	}
+}
+Test.AnotherWay._test_object_t.prototype.eq=function( got, expected, name )
+{
+	var msg={};
+	if( Test.AnotherWay._thing_eq( expected, got, "", msg ) ) {
+		this.ok( 1, name );
+	}else {
+		this.fail( name+". "+msg.msg );
+	}
+}
+Test.AnotherWay._test_object_t.prototype.like=function( got, expected, name )
+{
+	if( got.match( expected )!=null ) {
+		this.ok( 1, name );
+	}else {
+		this.fail( name+": got "+got+", but expected it to match: "+expected );
+	}
+}
+Test.AnotherWay._g_html_eq_span=null;
+Test.AnotherWay._html_eq_string_to_node=function( string_or_node, what, msg )
+{
+	if( string_or_node.nodeType!=null ) {
+		string_or_node=Test.AnotherWay._html_eq_node_to_string( string_or_node ); // double trip - to make properties assigned in scripts available as html node attributes
+	}
+	if( Test.AnotherWay._g_html_eq_span==null ) {
+		Test.AnotherWay._g_html_eq_span=document.createElement( "span" );
+	}
+	Test.AnotherWay._g_html_eq_span.innerHTML=string_or_node;
+	if( Test.AnotherWay._g_html_eq_span.childNodes.length!=1 ) {
+		msg.msg="bad "+what+" html string given (should contain exactly one outermost element): "+string_or_node;
+	}
+	return Test.AnotherWay._g_html_eq_span.childNodes[0].cloneNode( true );
+}
+Test.AnotherWay._html_eq_node_to_string=function( node ) {
+	if( Test.AnotherWay._g_html_eq_span==null ) {
+		Test.AnotherWay._g_html_eq_span=document.createElement( "span" );
+	}
+	Test.AnotherWay._g_html_eq_span.innerHTML="";
+	if( node.outerHTML!=null ) {
+		Test.AnotherWay._g_html_eq_span.innerHTML=node.outerHTML;
+	}else {
+		Test.AnotherWay._g_html_eq_span.appendChild( node.cloneNode( true ) );
+	}
+	return Test.AnotherWay._g_html_eq_span.innerHTML;
+}
+Test.AnotherWay._html_eq_path_msg=function( path )
+{
+	var msg="";
+	for( var i=0; i<path.length; ++i ) {
+		msg+=" [node "+path[i].node;
+		if( path[i].id!=null && path[i].id!="" ) {
+			msg+=" id "+path[i].id;
+		}else if( path[i].index!=null ) {
+			msg+=" at index "+path[i].index;
+		}
+		msg+="] "
+	}
+	return msg;
+}
+Test.AnotherWay._html_eq_fail_msg=function( path, what, expected, got )
+{
+	return Test.AnotherWay._html_eq_path_msg( path )+": "+what+" differ: got "+got+", but expected "+expected;
+}
+Test.AnotherWay._html_eq_remove_blank=function( text )
+{
+	if( text==null ) {
+		return "";
+	}else if( text.match( "^(\\s*)(.*\\S)(\\s*)$" ) ) {
+		return RegExp.$2;
+	}else if( text.match( "\s*" ) ) {
+		return "";
+	}
+	return text;
+}
+Test.AnotherWay._html_eq_remove_blank_nodes=function( node )
+{
+	var to_remove=[];
+	for( var child=node.firstChild; child!=null; child=child.nextSibling ) {
+		if( child.nodeType==3 ) {
+			var value=Test.AnotherWay._html_eq_remove_blank( child.nodeValue );
+			if( value=="" ) {
+				to_remove.push( child );
+			}else {
+				child.nodeValue=value;
+			}
+		}
+	}
+	for( var i=0; i<to_remove.length; ++i ) {
+		node.removeChild( to_remove[i] );
+	}
+}
+Test.AnotherWay._html_node_type_text=function( node_type )
+{
+	if( node_type==1 ) {
+		return "1 (html element)";
+	}else if( node_type==3 ) {
+		return "3 (text)";
+	}else {
+		return node_type;
+	}
+}
+Test.AnotherWay._html_eq_node=function( expected, got, path, msg, expected_loc_base, got_loc_base )
+{
+	if( expected.nodeType!=got.nodeType ) {
+		msg.msg=Test.AnotherWay._html_eq_fail_msg( path, "node types", Test.AnotherWay._html_node_type_text( expected.nodeType ), Test.AnotherWay._html_node_type_text( got.nodeType ) );
+		return false;
+	}else if( expected.nodeType==3 ) {
+		if( expected.nodeValue!=got.nodeValue ) {
+			msg.msg=Test.AnotherWay._html_eq_fail_msg( path, "text", expected.nodeValue, got.nodeValue );
+			return false;
+		}
+	}else if( expected.nodeType==1 ) {
+		if( expected.nodeName!=got.nodeName ) {
+			msg.msg=Test.AnotherWay._html_eq_fail_msg( path, "node names", expected.nodeName, got.nodeName );
+			return false;
+		}
+		// compare attributes
+		var expected_attrs={};
+		var got_attrs={};
+		var i;
+		var a;
+		for( i=0; i<expected.attributes.length; ++i ) {
+			a=expected.attributes[i];
+			if( a.specified ) {
+				expected_attrs[a.name]=1;
+			}
+		}
+		for( i=0; i<got.attributes.length; ++i ) {
+			a=got.attributes[i];
+			if( a.specified ) {
+				got_attrs[a.name]=1;
+			}
+		}
+		for( a in expected_attrs ) {
+			if( ! (a in got_attrs) ) {
+				msg.msg=Test.AnotherWay._html_eq_path_msg( path )+": attribute sets differ: expected attribute "+a+" is missing";
+				return false;
+			}
+		}
+		for( a in got_attrs ) {
+			if( ! (a in expected_attrs) ) {
+				msg.msg=Test.AnotherWay._html_eq_path_msg( path )+": attribute sets differ: got extra attribute "+a;
+				return false;
+			}
+		}
+		for( a in expected_attrs ) {
+			var expected_value=expected.getAttribute( a );
+			var got_value=got.getAttribute( a );
+			if( typeof( expected_value )=="string" && typeof( got_value )=="string" ) {
+				expected_value=Test.AnotherWay._html_eq_remove_blank( expected_value );
+				got_value=Test.AnotherWay._html_eq_remove_blank( got_value );
+				var ok=expected_value==got_value;
+				if( !ok && (a=="href" || a=="HREF" )  ) { // try relative hrefs
+					var expected_relative_value=expected_value;
+					if( expected_loc_base!=null && expected_value.substring( 0, expected_loc_base.length )==expected_loc_base ) {
+						expected_relative_value=expected_value.substring( expected_loc_base.length );
+					}
+					var got_relative_value=got_value;
+					if( got_loc_base!=null && got_value.substring( 0, got_loc_base.length )==got_loc_base ) {
+						got_relative_value=got_value.substring( got_loc_base.length );
+					}
+					ok=expected_relative_value==got_relative_value;
+				}
+				if( !ok ) {
+					msg.msg=Test.AnotherWay._html_eq_fail_msg( path, "attribute "+a+" values", expected_value, got_value );
+					return false;
+				}
+			}else if( typeof( expected_value )=="function" && typeof( got_value )=="function" ) {
+				expected_value=expected_value.toString();
+				got_value=got_value.toString();
+				if( expected_value!=got_value ) {
+					msg.msg=Test.AnotherWay._html_eq_fail_msg( path, "attribute "+a+" values", expected_value, got_value );
+					return false;
+				}
+			}else {
+				var value_msg={};
+				if( !Test.AnotherWay._thing_eq( expected_value, got_value, "", value_msg ) ) {
+					msg.msg=Test.AnotherWay._html_eq_path_msg( path )+": attribute "+a+" values differ: "+value_msg.msg;
+					return false;
+				}
+			}
+		}
+		// compare child nodes
+		Test.AnotherWay._html_eq_remove_blank_nodes( expected );
+		Test.AnotherWay._html_eq_remove_blank_nodes( got );
+		var expected_length=expected.childNodes.length;
+		var got_length=got.childNodes.length;
+		if( expected_length<got_length ) {
+			msg.msg=Test.AnotherWay._html_eq_path_msg( path )+": got "+(got_length-expected_length)+" extra child nodes";
+			return false;
+		}else if( expected_length>got_length ) {
+			msg.msg=Test.AnotherWay._html_eq_path_msg( path )+": expected "+(expected_length-got_length)+" more child nodes";
+			return false;
+		}else {
+			for( i=0; i<expected_length; ++i ) {
+				var expected_node=expected.childNodes[i];
+				path.push( { node: expected_node.nodeName, id: expected_node.id, index: i } );
+				var eq=Test.AnotherWay._html_eq_node( expected_node, got.childNodes[i], path, msg, expected_loc_base, got_loc_base );
+				path.pop();
+				if( !eq ) {
+					return false;
+				}
+			}
+		}
+	}
+	return true;
+}
+Test.AnotherWay._html_eq_get_loc_base=function( node )
+{
+	var loc_base=document.location;
+	if( node.ownerDocument!=null ) {
+		loc_base=node.ownerDocument.location;
+	}
+	if( loc_base!=null ) {
+		loc_base=loc_base.href;
+		var slash_pos=loc_base.lastIndexOf( "/" );
+		if( slash_pos!=-1 ) {
+			loc_base=loc_base.substring( 0, slash_pos+1 );
+		}
+	}
+	return loc_base;
+}
+Test.AnotherWay._test_object_t.prototype.html_eq=function( got, expected, name )
+{
+	var msg={};
+	var expected_node=Test.AnotherWay._html_eq_string_to_node( expected, "expected", msg );
+	if( msg.msg!=null ) {
+		this.fail( name+" html_eq: "+msg.msg );
+	}else {
+		var got_node=Test.AnotherWay._html_eq_string_to_node( got, "got", msg );
+		if( msg.msg!=null ) {
+			this.fail( name+" html_eq: "+msg.msg );
+		}else {
+			var expected_loc_base=Test.AnotherWay._html_eq_get_loc_base( expected );
+			var got_loc_base=Test.AnotherWay._html_eq_get_loc_base( got );
+			if( Test.AnotherWay._html_eq_node( expected_node, got_node, [], msg, expected_loc_base, got_loc_base ) ) {
+				this.ok( 1, name );
+			}else {
+				var msg=name+" html_eq "+msg.msg;
+				var expected_str=Test.AnotherWay._html_eq_node_to_string( expected_node );
+				var got_str=Test.AnotherWay._html_eq_node_to_string( got_node );
+				msg+=".\n got html: "+got_str;
+				msg+=".\n expected html: "+expected_str;
+				this.fail( msg );
+			}
+		}
+	}
+}
+Test.AnotherWay._debug_pane_print=function( msg )
+{
+	var d=new Date();
+	var p=document.createElement( "p" );
+	p.appendChild( document.createTextNode( d.toLocaleTimeString()+" "+msg ) );
+	var debug_pane=document.getElementById( "debug" );
+	debug_pane.appendChild( p );
+	var debug_tab=document.getElementById( "debug_tab" );
+	var results_tab=document.getElementById( "results_tab" );
+	debug_tab.style.visibility="visible";
+	results_tab.style.visibility="visible";
+}
+Test.AnotherWay._test_object_t.prototype.debug_print=function( msg )
+{
+	Test.AnotherWay._debug_pane_print( this.name+": "+msg );
+}
+Test.AnotherWay._test_object_t.prototype.delay_call=function()
+{
+	var timeout_ms=200;
+	for( var i=0; i<arguments.length; ++i ) {
+		if( typeof( arguments[i] )!="function" ) {
+			timeout_ms=1000*arguments[i];
+		}else {
+			var action={ action_kind: "call", call_delay_milliseconds: timeout_ms, call_fn: arguments[i] };
+			this.delay_total_milliseconds_left+=Test.AnotherWay._action_estimate_milliseconds( action );
+			this.delay_actions.push( action );
+		}
+	}
+}
+Test.AnotherWay._test_object_t.prototype.open_window=function( url, fn, timeout_seconds )
+{
+	if( timeout_seconds==null ) {
+		timeout_seconds=4;
+	}
+	var no_close=document.getElementById( "dont_close_test_windows" );
+	var action={ action_kind: "window", wnd_url: url.toString(), wnd_wnd: null, wnd_fn: fn, wnd_timeout_milliseconds: timeout_seconds*1000, wnd_no_close: no_close.checked };
+	this.delay_total_milliseconds_left+=Test.AnotherWay._action_estimate_milliseconds( action );
+	this.delay_actions.push( action );
+}
+Test.AnotherWay._test_object_t.prototype.replay_events=function( wnd, events )
+{
+	if( Test.AnotherWay._g_no_record_msg!=null ) {
+		this.fail( "replay_events: "+Test.AnotherWay._g_no_record_msg );
+	}else {
+		var action={ action_kind: "replay", replay_wnd: wnd, replay_events: events.events, replay_event_i: null, replay_checkpoints: events.checkpoints };
+		this.delay_total_milliseconds_left+=Test.AnotherWay._action_estimate_milliseconds( action );
+		this.delay_actions.push( action );
+	}
+}
+Test.AnotherWay._action_estimate_milliseconds=function( action )
+{
+	var ms=0;
+	if( action.action_kind=="call" ) {
+		ms=action.call_delay_milliseconds;
+	}else if( action.action_kind=="window" ) {
+		ms=0;
+	}else if( action.action_kind=="replay" ) {
+		ms=0;
+		for( var i=0; i<action.replay_events.length; ++i ) {
+			ms+=action.replay_events[i]["time"]-0;
+		}
+	}
+	return ms;
+}
+
+Test.AnotherWay._g_timeout_granularity=200;
+Test.AnotherWay._g_tests_queue=[]; // vector of { url: string, test_objects : array of test_object_t, test_object_i: int, wait_msg: <p> object, loading_timeout_milliseconds: int, timeout_id: id }
+
+// load one html page, schedule further processing
+Test.AnotherWay._run_test_page=function( id, called_from_outside )
+{
+	if( id.match( /^test(\d+)/ ) ) {
+		id=RegExp.$1;
+		Test.AnotherWay._g_tests_queue.push( {
+			url: Test.AnotherWay._g_test_page_urls[id].url,
+			convention: Test.AnotherWay._g_test_page_urls[id].convention,
+			test_objects: []
+			} );
+		if( Test.AnotherWay._g_tests_queue.length==1 ) {
+			if( !called_from_outside ) {
+				// Crap. Be careful stepping around.
+				// For Mozilla and Opera, when this file is included into the frameset page that is in another directory (and _g_outside_path_correction!=null)
+				// but the test pages are started from within it (by "run" buttons), then:
+				// depending on whether the page is the first one loaded into the test frame or not,
+				// the base url for relative test pages differs.
+				// Crap, like I said.
+				Test.AnotherWay._g_tests_queue[0].suppress_outside_path_correction=true;
+			}
+			Test.AnotherWay._start_loading_page();
+		}
+	}
+}
+Test.AnotherWay._load_next_page=function()
+{
+	Test.AnotherWay._g_tests_queue.splice( 0, 1 );
+	if( Test.AnotherWay._g_tests_queue.length>0 ) {
+		Test.AnotherWay._start_loading_page();
+	}else {
+		if( !Test.AnotherWay._g_test_frame_no_clear ) {
+			Test.AnotherWay._g_test_iframe.location.replace( "about:blank" );
+		}
+	}
+}
+Test.AnotherWay._g_opera_path_correction=null; // ugly wart to support opera
+Test.AnotherWay._g_outside_path_correction=null; // ugly wart to accomodate Opera and Mozilla, where relative url relates to the directory where the page that calls this function is located
+Test.AnotherWay._set_iframe_location=function( iframe, loc, outside_path_correction )
+{
+	// allow to load only locations with the same origin
+	var proto_end=loc.indexOf( "://" );
+	if( proto_end!=-1 ) { // otherwise, it's safe to assume (for Opera, Mozilla and IE ) that loc will be treated as relative
+		var main_loc=window.location.href;
+		var host_end=loc.substring( proto_end+3 ).indexOf( "/" );
+		var ok=false;
+		if( host_end!=-1 ) {
+			var loc_origin=loc.substring( 0, proto_end+3+host_end+1 );
+			if( main_loc.length>=loc_origin.length && main_loc.substring( 0, loc_origin.length )==loc_origin ) {
+				ok=true;
+			}
+		}
+		if( !ok ) {
+			return { msg: "test pages may have only urls with the same origin as "+main_loc };
+		}
+	}
+	// opera cannot handle urls relative to file:// without assistance
+	if( window.opera!=null && window.location.protocol=="file:" && loc.indexOf( ":" )==-1 ) {
+		var base=window.location.href;
+		var q_pos=base.indexOf( "?" );
+		if( q_pos!=-1 ) {
+			base=base.substring( 0, q_pos );
+		}
+		var slash_pos=base.lastIndexOf( "/" );
+		if( slash_pos!=-1 ) {
+			base=base.substring( 0, slash_pos+1 );
+			Test.AnotherWay._g_opera_path_correction=base;
+			loc=base+loc;
+		}
+	}
+	// if this function is called from another page, and if that page is in another directory, correction is needed
+	if( outside_path_correction!=null ) {
+		var pos=loc.indexOf( outside_path_correction );
+		if( pos==0 ) {
+			loc=loc.substring( outside_path_correction.length+1 );
+		}
+	}
+	if( iframe.location!=null ) {
+		iframe.location.replace( loc );
+	}else {
+		iframe.src=loc;
+	}
+	return {};
+}
+Test.AnotherWay._start_loading_page=function()
+{
+	var test_page=Test.AnotherWay._g_tests_queue[0];
+	test_page.loading_timeout_milliseconds=6000;
+	test_page.timeout_id=setTimeout( Test.AnotherWay._loading_timeout, Test.AnotherWay._g_timeout_granularity );
+	test_page.wait_msg=Test.AnotherWay._print_counter_result( test_page.url, "loading...", test_page.loading_timeout_milliseconds, "loading" );
+	if( test_page.convention=="jsan" ) {
+		// the tests in that page will run when it's loading, so the test object must be ready
+		Test.AnotherWay._g_test_object_for_jsan=new Test.AnotherWay._test_object_t( test_page.url );
+	}
+	var outside_path_correction=null;
+	if( Test.AnotherWay._g_outside_path_correction!=null && !test_page.suppress_outside_path_correction ) {
+		outside_path_correction=Test.AnotherWay._g_outside_path_correction;
+	}
+	var result=Test.AnotherWay._set_iframe_location( Test.AnotherWay._g_test_iframe, test_page.url, outside_path_correction );
+	if( result.msg!=null ) {
+		Test.AnotherWay._unprint_result( test_page.wait_msg );
+		Test.AnotherWay._print_result( test_page.url, result.msg, "badtest", null );
+		Test.AnotherWay._load_next_page();
+	}
+}
+
+Test.AnotherWay._loading_timeout=function()
+{
+	var test_page=Test.AnotherWay._g_tests_queue[0];
+	test_page.loading_timeout_milliseconds-=Test.AnotherWay._g_timeout_granularity;
+	if( test_page.loading_timeout_milliseconds>0 ) {
+		Test.AnotherWay._update_msg_counter( test_page.wait_msg, (test_page.loading_timeout_milliseconds/1000).toFixed() );
+		test_page.timeout_id=setTimeout( Test.AnotherWay._loading_timeout, Test.AnotherWay._g_timeout_granularity );
+	}else {
+		Test.AnotherWay._unprint_result( test_page.wait_msg );
+		Test.AnotherWay._print_result( test_page.url, "Unable to load test page. Timeout expired", "badtest", null );
+		Test.AnotherWay._load_next_page();
+	}
+}
+
+Test.AnotherWay._strip_query_and_hash=function( s )
+{
+	var i=s.lastIndexOf( "#" );
+	if( i!=-1 ) {
+		s=s.substring( 0, i );
+	}
+	i=s.lastIndexOf( "?" );
+	if( i!=-1 ) {
+		s=s.substring( 0, i );
+	}
+	return s;
+}
+Test.AnotherWay._is_url_loaded=function( url, wnd )
+{
+	var loaded=false;
+	if( wnd!=null && wnd.location!=null ) {
+		// after some popup blocker interference, location may behave strange..
+		var location_s="";
+		location_s+=wnd.location;
+		if( location_s!="" ) {
+			var pathname=wnd.location.pathname;
+			var expected_url=url;
+			var i=expected_url.lastIndexOf( "#" );
+			if( i!=-1 ) {
+				expected_url=expected_url.substring( 0, i );
+			}
+			i=expected_url.lastIndexOf( "?" );
+			if( i!=-1 ) {
+				expected_url=expected_url.substring( 0, i );
+			}
+			i=expected_url.lastIndexOf( "/" );
+			if( i!=-1 && i!=expected_url.length-1 ) {
+				expected_url=expected_url.substring( i+1 );
+			}
+			i=pathname.indexOf( expected_url )
+			if( wnd.location.href==url || (i!=-1 && i==pathname.length-expected_url.length) ) {
+				if( /*window.opera==null*/wnd.document.readyState==null || wnd.document.readyState=="complete" ) { // for opera (and IE?), getElementById does not work until..
+					loaded=true;
+				}
+			}
+		}
+	}
+	return loaded;
+}
+// find and run all test functions in the g_cur_page html page.
+Test.AnotherWay._test_page_onload=function()
+{
+	if( Test.AnotherWay._g_tests_queue.length==0 ) {
+		return;
+	}
+	var test_page=Test.AnotherWay._g_tests_queue[0];
+	if( !Test.AnotherWay._is_url_loaded( test_page.url, Test.AnotherWay._g_test_iframe ) ) {
+		return;
+	}
+	clearTimeout( test_page.timeout_id );
+	Test.AnotherWay._unprint_result( test_page.wait_msg );
+
+	if( test_page.convention=="anotherway" ) {
+		// get test function names (those beginning with "test")
+		if( typeof( Test.AnotherWay._g_test_iframe.document.scripts )!='undefined' ) { // IE
+			for( var i=0; i<Test.AnotherWay._g_test_iframe.document.scripts.length; ++i ) {
+				var script_text=Test.AnotherWay._g_test_iframe.document.scripts[i].text;
+				var fun_sig="function test";
+				var fun_start=script_text.indexOf( fun_sig );
+
+				while( fun_start!=-1 ) {
+					script_text=script_text.substring( fun_start, script_text.length );
+					var fun_end=script_text.indexOf( '(' );
+					var fun_name=script_text.substring( "function ".length,  fun_end );
+                                        var whitespace = fun_name.indexOf( ' ' );
+                                        if (whitespace >= 0)
+                                            fun_name = fun_name.substring( 0, whitespace );
+					test_page.test_objects.push( new Test.AnotherWay._test_object_t( fun_name ) );
+					script_text=script_text.substring( fun_end, script_text.length );
+					fun_start=script_text.indexOf( fun_sig  );
+				}
+			}
+		}else { // otherwise (not IE) it ought to work like this
+			for( var i in Test.AnotherWay._g_test_iframe) {
+				if( typeof( Test.AnotherWay._g_test_iframe[i] )=='function' ) {
+					if( i.substring( 0, 4 )=="test" ) {
+						test_page.test_objects.push( new Test.AnotherWay._test_object_t( i ) );
+					}
+				}
+			}
+		}
+	}else if( test_page.convention=="jsan" ) {
+		// the test object is already filled with results
+		test_page.test_objects.push( Test.AnotherWay._g_test_object_for_jsan );
+	}
+
+	if( test_page.test_objects.length==0 ) {
+		Test.AnotherWay._print_result( test_page.url,  "No test functions defined in the page", "badtest", null );
+		Test.AnotherWay._load_next_page();
+		return;
+	}
+
+	test_page.wait_msg=Test.AnotherWay._print_result( test_page.url, "running tests..<span class=\"counter\">"+test_page.test_objects.length+"</span>", "running", null );
+
+	test_page.test_object_i=0;
+	Test.AnotherWay._run_more_tests();
+}
+
+Test.AnotherWay._handle_exception=function( o, e, title )
+{
+	var s=title+": "+typeof( e )+": ";
+	if( e.message!=null ) {
+		s+=e.message;
+	}else if( e.description!=null ) {
+		s+=e.description;
+	}else {
+		s+=e.toString();
+	}
+//	if( e.location!=null ) {  // XXX figure out how to display exception location if it's present (like in mozilla)
+//		s+=" location: "+e.location.toString();
+//	}
+	o.exception=s;
+	s=[];
+	if( e.stack ) {
+		var lines=e.stack.split( "\n" );
+		for( var i=0; i<lines.length; ++i ) {
+			// format of the line: func_name(args)@file_name:line_no
+			if( lines[i].match( /(\w*)\(([^\)]*)\)@(.*):([^:]*)$/ ) ) {
+				var func_name=RegExp.$1;
+				if( func_name.length==0 ) {
+					func_name="<anonymous>";
+				}
+				s.push( "in "+func_name+"( "+RegExp.$2+") at "+RegExp.$3+" line "+RegExp.$4+"\n" );
+			}
+		}
+	}
+	o.exception_stack=s;
+}
+
+Test.AnotherWay._run_more_tests=function()
+{
+	var test_page=Test.AnotherWay._g_tests_queue[0];
+	while( test_page.test_object_i<test_page.test_objects.length ) {
+		Test.AnotherWay._update_msg_counter( test_page.wait_msg, (1+test_page.test_object_i)+"/"+test_page.test_objects.length );
+		var o=test_page.test_objects[test_page.test_object_i];
+		if( test_page.convention=="anotherway" ) {
+			try {
+				Test.AnotherWay._g_test_iframe[o.name]( o );
+			}catch( e ) {
+				Test.AnotherWay._handle_exception( o, e, "" );
+			}
+		} // for "jsan" convention, test has run already
+		if( o.delay_actions.length>0 || o.wait_result_milliseconds>0 ) {
+			o.delay_total_milliseconds_left+=o.wait_result_milliseconds;
+			Test.AnotherWay._delay_actions_timeout();
+			return;
+		}
+		++test_page.test_object_i;
+	}
+	Test.AnotherWay._unprint_result( test_page.wait_msg );
+	Test.AnotherWay._print_result( test_page.url, null, null, test_page.test_objects );
+	Test.AnotherWay._load_next_page();
+}
+
+Test.AnotherWay._delay_actions_timeout=function()
+{
+	var test_page=Test.AnotherWay._g_tests_queue[0];
+	var test_object=test_page.test_objects[test_page.test_object_i];
+	var finished=true;
+	if( test_object.delay_action_i==null ) {
+		// set up to start first action
+		test_object.delay_action_i=-1;
+	}else {
+		// perform current action
+		var milliseconds_passed=(new Date()).getTime()-test_object.delay_prev_timer_time;
+		test_object.delay_current_milliseconds_left-=milliseconds_passed;
+		test_object.delay_total_milliseconds_left-=milliseconds_passed;
+		finished=Test.AnotherWay._delay_continue_action( test_object, milliseconds_passed );
+	}
+	while( finished && test_object.delay_action_i<test_object.delay_actions.length ) {
+		++test_object.delay_action_i; // start next action
+		finished=Test.AnotherWay._delay_start_action( test_object );
+	}
+	if( test_object.delay_action_i<=test_object.delay_actions.length ) { // any more actions left ?
+		test_object.delay_prev_timer_time=(new Date()).getTime();
+		var next_timeout=Test.AnotherWay._g_timeout_granularity;
+		if( test_object.delay_current_milliseconds_left<next_timeout ) {
+			next_timeout=test_object.delay_current_milliseconds_left;
+		}
+		if( test_object.second_wait_msg!=null ) {
+			Test.AnotherWay._update_msg_counter( test_object.second_wait_msg, (test_object.delay_total_milliseconds_left/1000).toFixed() );
+		}
+		setTimeout( Test.AnotherWay._delay_actions_timeout, next_timeout );
+	}else { // no more actions left. run the next test.
+		if( test_object.second_wait_msg!=null ) {
+			Test.AnotherWay._unprint_result( test_object.second_wait_msg );
+			test_object.second_wait_msg=null;
+		}
+		++test_page.test_object_i;
+		Test.AnotherWay._run_more_tests();
+	}
+}
+Test.AnotherWay._delay_start_action=function( test_object )
+{
+	var finished=false;
+	var wait_msg="";
+	if( test_object.delay_action_i==test_object.delay_actions.length ) {
+		if( test_object.wait_result_milliseconds>0 ) {
+			test_object.delay_current_milliseconds_left=test_object.wait_result_milliseconds; // wait for result
+			wait_msg="waiting for results..";
+		}else {
+			++test_object.delay_action_i; // dont wait for result
+		}
+	}else {
+		var action=test_object.delay_actions[test_object.delay_action_i];
+		if( action.action_kind=="call" ) {
+			test_object.delay_current_milliseconds_left=action.call_delay_milliseconds;
+			wait_msg="performing delayed calls..";
+		}else if( action.action_kind=="window" ) {
+			if( Test.AnotherWay._g_opera_path_correction!=null && action.wnd_url.indexOf( ":" )==-1 ) {
+				action.wnd_url=Test.AnotherWay._g_opera_path_correction+action.wnd_url;
+			}
+			action.wnd_wnd=window.open( action.wnd_url, "_blank" );
+			if( action.wnd_wnd==null ) {
+				finished=true;
+				test_object.fail( "unable to open window for "+action.wnd_url );
+			}else {
+				test_object.delay_current_milliseconds_left=action.wnd_timeout_milliseconds;
+				wait_msg="opening window..";
+			}
+		}else if( action.action_kind=="replay" ) {
+			if( action.replay_events.length==0 ) {
+				finished=true;
+			}else {
+				action.replay_event_i=0;
+				test_object.delay_current_milliseconds_left=action.replay_events[0]["time"];
+				wait_msg="replaying events..";
+			}
+		}
+	}
+	if( test_object.second_wait_msg!=null ) {
+		Test.AnotherWay._unprint_result( test_object.second_wait_msg );
+	}
+	if( wait_msg!="" ) {
+		var test_page=Test.AnotherWay._g_tests_queue[0];
+		test_object.second_wait_msg=Test.AnotherWay._print_counter_result( test_page.url, wait_msg, test_object.delay_total_milliseconds_left, "waiting" );
+	}else {
+		test_object.second_wait_msg=null;
+	}
+	return finished;
+}
+Test.AnotherWay._delay_continue_action=function( test_object, milliseconds_passed )
+{
+	var finished=test_object.delay_current_milliseconds_left<=0;
+	if( test_object.delay_action_i==test_object.delay_actions.length ) { // action is "waiting for results"
+		if( test_object.n_plan!=null && test_object.n_plan==test_object.n_ok+test_object.n_fail ) {
+			finished=true; // if all assertions results are recorded, don't wait any more
+		}
+		if( finished ) {
+			++test_object.delay_action_i; // move on to the next test
+		}
+	}else {
+		var action=test_object.delay_actions[test_object.delay_action_i];
+		if( action.action_kind=="call" ) {
+			if( finished ) {
+				try {
+					action.call_fn();
+				}catch( e ) {
+					Test.AnotherWay._handle_exception( test_object, e, "in delay_call" );
+				}
+			}
+		}else if( action.action_kind=="window" ) {
+			test_object.delay_total_milliseconds_left+=milliseconds_passed; // for "window", the countdown is suspended since it's unknown how long it will take
+			if( Test.AnotherWay._is_url_loaded( action.wnd_url, action.wnd_wnd ) ) {
+				try {
+					action.wnd_fn( action.wnd_wnd );
+				}catch( e ) {
+					Test.AnotherWay._handle_exception( test_object, e, "in open_window function call" );
+				}
+				finished=true;
+			}else if( finished ) {
+				test_object.fail(  "unable to open window for url '"+action.wnd_url+"'. timeout expired" );
+			}
+		}else if( action.action_kind=="replay" ) {
+			if( finished ) {
+//				try {
+					Test.AnotherWay._delay_replay_event( test_object, action.replay_wnd, action.replay_events[action.replay_event_i], action.replay_checkpoints );
+//				}catch( e ) { // disabled, until I know how to gel location info from an exception
+//					Test.AnotherWay._handle_exception( test_object, e, "while replaying event" );
+//				}
+				++action.replay_event_i;
+				finished=action.replay_event_i==action.replay_events.length;
+				if( !finished ) {
+					test_object.delay_current_milliseconds_left=action.replay_events[action.replay_event_i]["time"];
+				}
+			}
+		}
+	}
+	return finished;
+}
+Test.AnotherWay._delay_replay_event=function( test_object, wnd, event, checkpoints )
+{
+	if( event.type=="_checkpoint" ) {
+		var checkpoint_n=event.which;
+		var prev_n_fail=test_object.n_fail;
+		checkpoints[checkpoint_n]( test_object, wnd );
+		var flash_color= prev_n_fail==test_object.n_fail ? "#2f2" : "#f22" ;
+		Test.AnotherWay._record_flash_border( flash_color );
+	}else if( event.type=="click" || event.type=="mouseover" || event.type=="mouseout" || event.type=="mousemove" || event.type=="mousedown" || event.type=="mouseup" ) {
+		var target=Test.AnotherWay._record_node_path_to_node( event["target"], wnd.document );
+		if( target!=null ) {
+			Test.AnotherWay._record_control_update_highlight( target, "ball", event );
+			var e=wnd.document.createEvent( "MouseEvents" );
+			var related_target=Test.AnotherWay._record_node_path_to_node( event["relatedTarget"], wnd.document );
+			e.initMouseEvent(
+				event["type"],
+				event["cancelable"],
+				event["bubbles"],
+				wnd.document.defaultView,
+				event["detail"],
+				event["screenX"],
+				event["screenY"],
+				event["clientX"],
+				event["clientY"],
+				event["ctrlKey"],
+				event["altKey"],
+				event["shiftKey"],
+				event["metaKey"],
+				event["button"],
+				Test.AnotherWay._record_node_path_to_node( event["relatedTarget"], wnd.document )
+			);
+			// Firefox 1.0.6 somehow loses relatedTarget somewhere on the way. Pass through our own, for those who choose to care.
+			e.passThroughRelatedTarget=related_target;
+			target.dispatchEvent( e );
+		}
+	}else if( event.type=="keyup" || event.type=="keydown" || event.type=="keypress" ) {
+		var e=wnd.document.createEvent( "KeyboardEvents" ); // forget it. Apparently it's not supported neither by mozilla nor by opera.
+		e.initKeyboardEvent(
+				event["type"],
+				event["cancelable"],
+				event["bubbles"],
+				wnd.document.defaultView,
+				event["which"],
+				event["which"],
+				event["ctrlKey"],
+				event["altKey"],
+				event["shiftKey"],
+				event["metaKey"],
+				false
+		);
+		wnd.document.dispatchEvent( e );
+	}
+}
+
+Test.AnotherWay._print_counter_result=function( url, msg, milliseconds, style )
+{
+	return Test.AnotherWay._print_result( url, msg+"<span class=\"counter\">"+(milliseconds/1000).toFixed()+"</span>", style, null );
+}
+
+Test.AnotherWay._g_result_count=0; // for assigning unique ids to result paragraphs
+
+// number of pages tested
+Test.AnotherWay._g_ok_pages=0;
+Test.AnotherWay._g_fail_pages=0;
+
+Test.AnotherWay._print_result=function( url, msg, style, test_objects )
+{
+	var results=document.getElementById( "results" );
+	var r=results.appendChild( document.createElement( "p" ) );
+	r.id="result"+Test.AnotherWay._g_result_count;
+	++Test.AnotherWay._g_result_count;
+	r.onclick=Test.AnotherWay._toggle_detail;
+	var text="<span class=\"bullet\">&nbsp;&nbsp;&nbsp;</span>&nbsp;";
+	if( url!="" ) {
+		text+=url+":  ";
+	}
+	if( msg!=null ) {
+		text+=msg;
+	}
+	if( test_objects!=null ) {
+		// compose summary and detail texts
+		var total_ok=0;
+		var total_detail_ok=0;
+		var total_fail=0;
+		var total_detail_fail=0;
+		var no_plan=0;
+
+		var detail=results.appendChild( document.createElement( "div" ) );
+
+		if( r.id.match( /^result(\d+)$/ ) ) {
+			detail.id="result_detail"+RegExp.$1;
+		}
+
+		for( var i=0; i<test_objects.length; ++i ) {
+			var o=test_objects[i];
+			var p;
+			var p_text;
+			p=document.createElement( "P" );
+			Test.AnotherWay._set_css_class( p, "result_detail" );
+			p_text=o.name;
+			if( o.n_fail>0 || o.exception || (o.n_plan!=null && o.n_plan!=o.n_ok+o.n_fail) || (o.n_plan==null && o.n_ok==0 && o.n_fail==0)) {
+				++total_fail;
+				p_text+=" <span class=\"fail\">";
+				if( o.n_plan!=null && o.n_plan!=o.n_ok+o.n_fail) {
+					p_text+="planned "+o.n_plan+" assertions but got "+(o.n_ok+o.n_fail)+"; ";
+				}
+				if(o.n_plan==null && o.n_ok==0 && o.n_fail==0) {
+					p_text+="test did not output anything";
+				}else {
+					p_text+=" fail "+o.n_fail;
+				}
+				p_text+="</span>";
+			}else {
+				++total_ok;
+			}
+			p_text+=" ok "+o.n_ok;
+			if( o.n_plan==null ) {
+				no_plan=1;
+				p_text+=" <span class=\"warning\">no plan</span>";
+			}
+			p.innerHTML=p_text;
+			detail.appendChild( p );
+			if( o.exception ) {
+				p=document.createElement( "P" );
+				Test.AnotherWay._set_css_class( p, "result_exception_detail" );
+				p.innerHTML="<span class=\"fail\">exception:</span> "+o.exception;
+				detail.appendChild( p );
+				p=document.createElement( "P" );
+				Test.AnotherWay._set_css_class( p, "result_exception_stack_detail" );
+				p.innerHTML=o.exception_stack.join( "<br/>" );
+				detail.appendChild( p );
+			}
+			for( var ii=0; ii<o.assertions.length; ++ii ) {
+				var oo=o.assertions[ii];
+				var status=oo.ok ? "ok" : "<span class=\"fail\">fail</span>";
+				p=document.createElement( "P" );
+				Test.AnotherWay._set_css_class( p, "result_micro_detail" );
+				p.innerHTML=status;
+				p.appendChild( document.createTextNode( " "+oo.name ) );
+				detail.appendChild( p );
+			}
+			total_detail_ok+=o.n_ok;
+			total_detail_fail+=o.n_fail;
+		}
+		if( total_fail || total_detail_fail ) {
+			text+=" fail "+total_fail;
+		}
+		text+=" ok "+total_ok+" (detailed:";
+		if( total_fail || total_detail_fail ) {
+			text+=" fail "+total_detail_fail;
+		}
+		text+=" ok "+total_detail_ok+")";
+		if( no_plan ) {
+			text+=" <span class=\"warning\">no plan</span>";
+		}
+		style= total_fail==0 ? "ok" : "fail";
+		detail.style.display= style=="fail" ? "block" : "none";
+		detail.style.cursor="text";
+	}
+	if( style!=null ) {
+		Test.AnotherWay._set_css_class( r, style );
+		if( style=="ok" ) {
+			++Test.AnotherWay._g_ok_pages;
+		}else if( style=="fail" || style=="badtest" ) {
+			++Test.AnotherWay._g_fail_pages;
+		}
+		var pages_total="";
+		if( Test.AnotherWay._g_fail_pages>0 ) {
+			pages_total+=" fail "+Test.AnotherWay._g_fail_pages;
+		}
+		pages_total+=" ok "+Test.AnotherWay._g_ok_pages;
+		Test.AnotherWay._update_results_total( pages_total );
+	}
+	r.innerHTML=text;
+	if( results.scrollHeight!=null && results.scrollTop!=null && results.offsetHeight!=null ) {
+		results.scrollTop=results.scrollHeight-results.offsetHeight;
+	}
+	// when test_objects is not null, the results are final - good time to clean up
+	if( test_objects!=null ) {
+		for( var i=0; i<test_objects.length; ++i ) {
+			var actions=test_objects[i].delay_actions;
+			for( var action_i=0; action_i<actions.length; ++action_i ) {
+				var action=actions[action_i];
+				if( action.action_kind=="window" && action.wnd_wnd!=null && !action.wnd_no_close ) {
+					action.wnd_wnd.close();
+					action.wnd_wnd=null;
+				}
+			}
+		}
+	}
+	return r;
+}
+Test.AnotherWay._unprint_result=function( child )
+{
+	var results=document.getElementById( "results" );
+	results.removeChild( child );
+}
+Test.AnotherWay._toggle_detail=function()
+{
+	if( this.id.match( /^result(\d+)$/ ) ) {
+		var detail=document.getElementById( "result_detail"+RegExp.$1 );
+		if( detail!=null ) {
+			if( detail.style.display=="none" ) {
+				detail.style.display="block";
+			}else if( detail.style.display=="block" ) {
+				detail.style.display="none";
+			}
+		}
+	}
+}
+Test.AnotherWay._update_msg_counter=function( msg, text )
+{
+	for( var i=0; i<msg.childNodes.length; ++i ) {
+		var item=msg.childNodes[i];
+		if( item.nodeName=="SPAN" && Test.AnotherWay._get_css_class( item )=="counter" ) {
+			item.innerHTML=text;
+		}
+	}
+}
+Test.AnotherWay._update_results_total=function( msg )
+{
+	var total=document.getElementById( "total" );
+	if( total ) {
+		total.innerHTML=msg;
+	}
+}
+Test.AnotherWay._results_clear_onclick=function()
+{
+	var results=document.getElementById( "results" );
+	results.innerHTML="";
+	Test.AnotherWay._update_results_total( "" );
+	Test.AnotherWay._g_ok_pages=0;
+	Test.AnotherWay._g_fail_pages=0;
+	var debug=document.getElementById( "debug" );
+	debug.innerHTML="";
+}
+
+Test.AnotherWay._get_css_class=function( o )
+{
+	var c=o.getAttribute( "className" );
+	if( c==null || c=="" ) {
+		c=o.getAttribute( "class" );
+	}
+	return c;
+}
+Test.AnotherWay._set_css_class=function( o, css_class )
+{
+	o.setAttribute( "className", css_class );
+	o.setAttribute( "class", css_class );
+}
+
+Test.AnotherWay._tab_onclick=function()
+{
+	var tab=this;
+	var tabs=[ document.getElementById( "debug_tab" ), document.getElementById( "results_tab" ) ];
+	var panes=[ document.getElementById( "debug" ), document.getElementById( "results" ) ];
+	for( var i=0; i<tabs.length; ++i ) {
+		if( tab==tabs[i] ) {
+			Test.AnotherWay._set_css_class( tabs[i], "active_tab" );
+			panes[i].style.display="block";
+		}else {
+			Test.AnotherWay._set_css_class( tabs[i], "inactive_tab" );
+			panes[i].style.display="none";
+		}
+	}
+}
+Test.AnotherWay._tab_mouseover=function()
+{
+	if( Test.AnotherWay._get_css_class( this )=="inactive_tab" ) {
+		Test.AnotherWay._set_css_class( this, "inactive_mouseover_tab" );
+	}
+}
+Test.AnotherWay._tab_mouseout=function()
+{
+	if( Test.AnotherWay._get_css_class( this )=="inactive_mouseover_tab" ) {
+		Test.AnotherWay._set_css_class( this, "inactive_tab" );
+	}
+}
+
+// recording mouse input
+Test.AnotherWay._record_check_onfocus=function()
+{
+	var o=this;
+	var check_select=o.type!="text";
+	var div=document.getElementById( "record_div" );
+	var inputs=div.getElementsByTagName( "input" );
+	for( var i=0; i<inputs.length; ++i ) {
+		var input=inputs[i];
+		if( input.type=="radio" ) {
+			if( input.value=="select" ) {
+				input.checked=check_select;
+			}else if( input.value=="input" ) {
+				input.checked=!check_select;
+			}
+		}
+	}
+}
+
+Test.AnotherWay._g_no_record_msg=null; // not null - recording is unavailable
+Test.AnotherWay._g_record_timeout_cnt=0; // opening window for a page for recording
+Test.AnotherWay._g_record_url=null;
+Test.AnotherWay._g_record_wnd=null;
+Test.AnotherWay._g_record_random_id=null; // added to element ids of record_control div so that they do not clash with ids already in the page for which input is recorded
+Test.AnotherWay._g_record_keydown=null; // recording control - which key is down
+Test.AnotherWay._g_record_ctrl_keydown=false;
+Test.AnotherWay._g_record_shift_keydown=false;
+Test.AnotherWay._g_record_control_visible=true; // recording control ui state
+Test.AnotherWay._g_record_started;
+Test.AnotherWay._g_record_paused;
+Test.AnotherWay._g_record_include_mousemove=false;
+Test.AnotherWay._g_record_start_time; // for time references
+Test.AnotherWay._g_record_pause_start_time;
+Test.AnotherWay._g_record_update_time_interval; // showing time in the control ui
+Test.AnotherWay._g_record_waiting_for_results=false; // waiting for results window to open
+Test.AnotherWay._g_record_events; // recorded events
+Test.AnotherWay._g_record_under_cursor; // track element under cursor
+Test.AnotherWay._g_record_checkpoint_count; // for checkpoint numbering
+Test.AnotherWay._g_record_mouse_over_record_control; // for avoiding record control highlight on mouseover
+Test.AnotherWay._g_record_highlighted_element={ element: null, x: null, y: null };
+
+Test.AnotherWay._record_control_get_element=function( id )
+{
+	if( Test.AnotherWay._g_record_wnd!=null && Test.AnotherWay._g_record_wnd.document!=null ) {
+		return Test.AnotherWay._g_record_wnd.document.getElementById( id+Test.AnotherWay._g_record_random_id );
+	}else {
+		return null;
+	}
+}
+Test.AnotherWay._record_start_onclick=function() // "record" button on the run_tests.html: open a window for a page for which input is recorded
+{
+	if( Test.AnotherWay._g_no_record_msg!=null ) {
+		alert( Test.AnotherWay._g_no_record_msg );
+		return;
+	}
+	if( Test.AnotherWay._g_record_timeout_cnt>0
+	    || (Test.AnotherWay._g_record_wnd!=null && (Test.AnotherWay._g_record_wnd.closed!=null && !Test.AnotherWay._g_record_wnd.closed)) ) { // in opera, closed is null.
+		alert( "there is already window opened for recording input for a page "+Test.AnotherWay._g_record_url );
+		return;
+	}
+	var div=document.getElementById( "record_div" );
+	var inputs=div.getElementsByTagName( "input" );
+	var url=null;
+	for( var i=0; i<inputs.length; ++i ) {
+		var input=inputs[i];
+		if( input.type=="radio" ) {
+			if( input.value=="select" && input.checked ) {
+				var index=document.getElementById( "record_select" ).selectedIndex;
+				if( index>0 ) {
+					url=Test.AnotherWay._g_test_page_urls[index-1].url;
+				}
+			}else if( input.value=="input" && input.checked ) {
+				url=document.getElementById( "record_input" ).value;
+			}
+		}
+	}
+	if( url!=null ) {
+		Test.AnotherWay._g_record_url=url;
+		Test.AnotherWay._g_record_wnd=window.open( url, "_blank" );
+		if( Test.AnotherWay._g_record_wnd==null ) {
+			alert( "unable to open new window for a page: "+url );
+		}else {
+			Test.AnotherWay._g_record_timeout_cnt=50;
+			setTimeout( Test.AnotherWay._record_window_timeout, 100 );
+		}
+	}
+}
+Test.AnotherWay._record_window_timeout=function()
+{
+	if( Test.AnotherWay._is_url_loaded( Test.AnotherWay._g_record_url, Test.AnotherWay._g_record_wnd ) ) {
+		Test.AnotherWay._record_window_setup( Test.AnotherWay._g_record_wnd );
+	}else {
+		if( --Test.AnotherWay._g_record_timeout_cnt>0 ) {
+			setTimeout( Test.AnotherWay._record_window_timeout, 100 );
+		}else {
+			alert( "timeout expired while opening new window for a page: "+Test.AnotherWay._g_record_url );
+			Test.AnotherWay._g_record_wnd=null;
+			Test.AnotherWay._g_record_url=null;
+			Test.AnotherWay._g_record_timeout_cnt=0;
+		}
+	}
+}
+Test.AnotherWay._record_control_randomize_id=function( e, r )
+{
+	if( e.id!="" ) {
+		e.id=e.id+r;
+	}
+	for( var c=e.firstChild; c!=null; c=c.nextSibling ) {
+		Test.AnotherWay._record_control_randomize_id( c, r );
+	}
+}
+Test.AnotherWay._record_window_setup=function( wnd ) // insert recording control into the page for which input is recorded
+{
+	Test.AnotherWay._g_record_timeout_cnt=0;
+	var this_div=document.getElementById( "record_control" );
+	var record_control=wnd.document.importNode( this_div, true );
+	Test.AnotherWay._g_record_random_id=(1000*Math.random()).toFixed();
+	Test.AnotherWay._record_control_randomize_id( record_control, Test.AnotherWay._g_record_random_id );
+	Test.AnotherWay._g_record_control_visible=true;
+	Test.AnotherWay._g_record_started=false;
+	Test.AnotherWay._g_record_paused=false;
+	Test.AnotherWay._g_record_checkpoint_count=0;
+	Test.AnotherWay._g_record_mouse_over_record_control=false;
+	var doc=wnd.document;
+	doc.body.appendChild( record_control );
+	// opera sans-serif font is different
+	if( window.opera ) {
+		cursor_over_indicator=Test.AnotherWay._record_control_get_element( "record_cursor_over" );
+		cursor_over_indicator.style.width="18em";
+		cursor_over_indicator.style.height="2em";
+		cursor_over_indicator.style.fontSize="7pt";
+	}
+	doc.addEventListener( "keydown", Test.AnotherWay._record_control_keydown, true );
+	doc.addEventListener( "keyup", Test.AnotherWay._record_control_keyup, true );
+//	doc.addEventListener( "keypress", Test.AnotherWay._record_event, true ); // replaying is not supported by any known browser
+
+	doc.body.addEventListener( "mousemove", Test.AnotherWay._record_on_mousemove, true );
+	doc.body.addEventListener( "click", Test.AnotherWay._record_event, true );
+	doc.body.addEventListener( "mouseover", Test.AnotherWay._record_event, true );
+	doc.body.addEventListener( "mouseout", Test.AnotherWay._record_event, true );
+	doc.body.addEventListener( "mousedown", Test.AnotherWay._record_event, true );
+	doc.body.addEventListener( "mouseup", Test.AnotherWay._record_event, true );
+}
+Test.AnotherWay._record_control_key_disabled=function( k )
+{
+	if( k=="c" ) {
+		return !Test.AnotherWay._g_record_started;
+	}else if( k=="p" ) {
+		return !Test.AnotherWay._g_record_started;
+	}else if( k=="s" ) {
+		return Test.AnotherWay._g_record_waiting_for_results;
+	}else {
+		return false;
+	}
+}
+
+Test.AnotherWay._record_control_update_ui=function()
+{
+	var keydown_color="#fff";
+	var disabled_color="#aaa";
+	var button_color="#adf";
+	var active_color="#fdf";
+
+	var display={};
+	display[false]="none";
+	display[true]="inline";
+
+	var s_button=Test.AnotherWay._record_control_get_element( "record_s" );
+	var record_on=Test.AnotherWay._record_control_get_element( "record_on" );
+	var record_off=Test.AnotherWay._record_control_get_element( "record_off" );
+
+	s_button.style.backgroundColor= Test.AnotherWay._record_control_key_disabled( "s" ) ? disabled_color
+		: Test.AnotherWay._g_record_keydown=="s" ? keydown_color : Test.AnotherWay._g_record_started ? active_color : button_color;
+	record_on.style.display=display[!Test.AnotherWay._g_record_started];
+	record_off.style.display=display[Test.AnotherWay._g_record_started];
+
+	var h_button=Test.AnotherWay._record_control_get_element( "record_h" );
+	h_button.style.backgroundColor= Test.AnotherWay._g_record_keydown=="h" ? keydown_color : button_color;
+
+	var p_button=Test.AnotherWay._record_control_get_element( "record_p" );
+	var record_pause_on=Test.AnotherWay._record_control_get_element( "record_pause_on" );
+	var record_pause_off=Test.AnotherWay._record_control_get_element( "record_pause_off" );
+	p_button.style.backgroundColor= Test.AnotherWay._record_control_key_disabled( "p" ) ? disabled_color
+		: Test.AnotherWay._g_record_keydown=="p" ? keydown_color : Test.AnotherWay._g_record_paused ? active_color : button_color;
+	record_pause_on.style.display=display[!Test.AnotherWay._g_record_paused];
+	record_pause_off.style.display=display[Test.AnotherWay._g_record_paused];
+
+	var m_button=Test.AnotherWay._record_control_get_element( "record_m" );
+	var record_include_mousemove=Test.AnotherWay._record_control_get_element( "record_include_mousemove" );
+	var record_omit_mousemove=Test.AnotherWay._record_control_get_element( "record_omit_mousemove" );
+	m_button.style.backgroundColor= Test.AnotherWay._g_record_keydown=="m" ? keydown_color : Test.AnotherWay._g_record_include_mousemove ? active_color : button_color;
+	record_include_mousemove.style.display=display[!Test.AnotherWay._g_record_include_mousemove];
+	record_omit_mousemove.style.display=display[Test.AnotherWay._g_record_include_mousemove];
+
+	var c_button=Test.AnotherWay._record_control_get_element( "record_c" );
+	c_button.style.backgroundColor= Test.AnotherWay._record_control_key_disabled( "c" ) ? disabled_color
+		: Test.AnotherWay._g_record_keydown=="c" ? keydown_color : button_color;
+
+	var record_indicator=Test.AnotherWay._record_control_get_element( "record_indicator" );
+	record_indicator.style.display=display[Test.AnotherWay._g_record_started];
+
+	var pause_indicator=Test.AnotherWay._record_control_get_element( "record_pause_indicator" );
+	pause_indicator.style.display=display[Test.AnotherWay._g_record_paused];
+
+	var record_control=Test.AnotherWay._record_control_get_element( "record_control" );
+	record_control.style.display= Test.AnotherWay._g_record_control_visible ? "block" : "none";
+
+	var shift_button=Test.AnotherWay._record_control_get_element( "record_shift_key" );
+	shift_button.style.backgroundColor= Test.AnotherWay._g_record_shift_keydown ? keydown_color : button_color;
+
+	var ctrl_button=Test.AnotherWay._record_control_get_element( "record_ctrl_key" );
+	ctrl_button.style.backgroundColor= Test.AnotherWay._g_record_ctrl_keydown ? keydown_color : button_color;
+}
+Test.AnotherWay._record_format_time=function( t )
+{
+	t=new Date( t );
+	var m=t.getMinutes();
+	var s=t.getSeconds();
+	var str= m==0 ? "" : m+"m ";
+	str+=s+"s.";
+	return str;
+}
+Test.AnotherWay._record_control_update_time=function()
+{
+	var time_display=Test.AnotherWay._record_control_get_element( "record_time" );
+	if( time_display!=null ) {
+		time_display.innerHTML=Test.AnotherWay._record_format_time( (new Date()).getTime()-Test.AnotherWay._g_record_start_time );
+	}
+}
+Test.AnotherWay._record_control_update_highlight=function( elem, style, event )
+{
+	if( elem==null ) {
+		Test.AnotherWay._record_highlight_border( null );
+	}else {
+		var pos=Test.AnotherWay._get_page_coords( elem );
+		if( style=="ball" || elem!=Test.AnotherWay._g_record_highlighted_element.element || pos.x!=Test.AnotherWay._g_record_highlighted_element.x || pos.y!=Test.AnotherWay._g_record_highlighted_element.y ) {
+			Test.AnotherWay._g_record_highlighted_element={ element: elem, x: pos.x, y: pos.y };
+			Test.AnotherWay._record_highlight_border( elem, style, event );
+		}
+	}
+}
+Test.AnotherWay._record_decode_key=function( event )
+{
+	var k=null;
+	if( event==null ) {
+		k=Test.AnotherWay._g_record_wnd.event.keyCode;
+	}else {
+		k=event.which;
+	}
+	if( k==83 ) {
+		return "s";
+	}else if( k==72 ) {
+		return "h";
+	}else if( k==73 ) {
+		return "i";
+	}else if( k==80 ) {
+		return "p";
+	}else if( k==67 ) {
+		return "c";
+	}else if( k==77 ) {
+		return "m";
+	}else if( k==16 ) {
+		return "shift";
+	}else if( k==17 ) {
+		return "ctrl";
+	}else if( k==18 ) {
+		return "alt";
+	}else if( k==19 ) {
+		return "pause";
+	}else if( k==123 ) {
+		return "f12";
+	}
+	return "";
+}
+Test.AnotherWay._record_control_keydown=function( event )
+{
+	var handled=false;
+	var k=Test.AnotherWay._record_decode_key( event );
+	if( k=="shift" ) {
+		Test.AnotherWay._g_record_shift_keydown=true;
+	}else if( k=="ctrl" ) {
+		Test.AnotherWay._g_record_ctrl_keydown=true;
+	}else if( k!="" && (Test.AnotherWay._g_record_keydown==null || Test.AnotherWay._g_record_keydown==k) ) {
+		if( Test.AnotherWay._g_record_ctrl_keydown && Test.AnotherWay._g_record_shift_keydown && !Test.AnotherWay._record_control_key_disabled( k ) ) {
+			Test.AnotherWay._g_record_keydown=k;
+			handled=true;
+		}
+	}else {
+		Test.AnotherWay._g_record_keydown="";
+	}
+	Test.AnotherWay._record_control_update_ui();
+	if( !handled ) {
+//		Test.AnotherWay._record_event( event ); // replaying is not supported in any known browser
+	}
+	return;
+}
+Test.AnotherWay._record_control_keyup=function( event )
+{
+	var handled=false;
+	var k=Test.AnotherWay._record_decode_key( event );
+	if( k=="shift" ) {
+		Test.AnotherWay._g_record_shift_keydown=false;
+	}else if( k=="ctrl" ) {
+		Test.AnotherWay._g_record_ctrl_keydown=false;
+	}else if( k!="" && k==Test.AnotherWay._g_record_keydown && Test.AnotherWay._g_record_ctrl_keydown && Test.AnotherWay._g_record_shift_keydown  ) {
+		if( k=="s" ) {
+			Test.AnotherWay._g_record_started=!Test.AnotherWay._g_record_started;
+			if( Test.AnotherWay._g_record_started ) {
+				Test.AnotherWay._g_record_events=[];
+				Test.AnotherWay._g_record_start_time=(new Date()).getTime();
+				Test.AnotherWay._record_control_update_time();
+				Test.AnotherWay._g_record_update_time_interval=window.setInterval( Test.AnotherWay._record_control_update_time, 200 );
+			}else {
+				Test.AnotherWay._record_control_update_highlight( null );
+				if( !Test.AnotherWay._g_record_paused ) {
+					window.clearInterval( Test.AnotherWay._g_record_update_time_interval );
+				}
+				Test.AnotherWay._g_record_waiting_for_results=true;
+				// open a new window for self, pass a parameter to dump recorded events as javascript code there
+				// (the easiest way to obtain a document from the same origin, so it's writable, is to open this same page again)
+				Test.AnotherWay._g_record_paused=false;
+				var loc=window.location;
+				loc=loc.protocol+"//"+loc.host+loc.pathname+"?recording_results="+Test.AnotherWay._g_record_random_id;
+				if( window.open( loc, "_blank" )==null ) {
+					alert( "unable to open new window for results" );
+				}
+			}
+			handled=true;
+		}else if( k=="h" ) {
+			Test.AnotherWay._g_record_control_visible=!Test.AnotherWay._g_record_control_visible;
+			handled=true;
+		}else if( k=="p" ) {
+			Test.AnotherWay._g_record_paused=!Test.AnotherWay._g_record_paused;
+			if( Test.AnotherWay._g_record_paused ) {
+				Test.AnotherWay._g_record_pause_start_time=(new Date()).getTime();
+				if( Test.AnotherWay._g_record_started ) {
+					window.clearInterval( Test.AnotherWay._g_record_update_time_interval );
+				}
+				Test.AnotherWay._record_control_update_highlight( null );
+			}else {
+				var pause_duration=(new Date()).getTime()-Test.AnotherWay._g_record_pause_start_time;
+				Test.AnotherWay._g_record_start_time+=pause_duration;
+				Test.AnotherWay._g_record_update_time_interval=window.setInterval( Test.AnotherWay._record_control_update_time, 200 );
+			}
+			handled=true;
+		}else if( k=="m" ) {
+			Test.AnotherWay._g_record_include_mousemove=!Test.AnotherWay._g_record_include_mousemove;
+			handled=true;
+		}else if( k=="c" ) {
+			var o=Test.AnotherWay._record_checkpoint();
+			Test.AnotherWay._record_display_checkpoint( o );
+			Test.AnotherWay._record_flash_border( "#24d" );
+			handled=true;
+		}
+	}
+	Test.AnotherWay._g_record_keydown=null;
+	Test.AnotherWay._record_control_update_ui();
+	if( !handled ) {
+//		Test.AnotherWay._record_event( event ); // replaying is not supported in any known browser
+	}
+	return;
+}
+Test.AnotherWay._record_html_node_path=function( node )
+{
+	if( node==null ) {
+		return null;
+	}
+	var path=[];
+	while( true ) {
+		if( node.id!=null && node.id!="" ) {
+			path.unshift( "#"+node.id+" "+node.nodeName );
+			break;
+		}else {
+			var parent_node=node.parentNode;
+			if( parent_node==null ) {
+				return []; // no BODY up the path - this node is screwed (browsers differ in what's above the body), discard
+			}else {
+				var i=0;
+				var found=false;
+				for( var child=parent_node.firstChild; child!=null; child=child.nextSibling ) {
+					if( child==node ) {
+						found=true;
+						break;
+					}
+					if( child.nodeType==1 ) { // count only HTML element nodes
+						++i;
+					}
+				}
+				if( !found ) {
+					i=-1;
+				}
+				path.unshift( i+" "+node.nodeName );
+				if( parent_node.nodeName=="BODY" || parent_node.nodeName=="body" ) {
+					break;
+				}
+				node=parent_node;
+			}
+		}
+	}
+	return path;
+}
+Test.AnotherWay._record_node_path_to_string=function( path )
+{
+	var s="";
+	if( path!=null ) {
+		for( var i=0; i<path.length; ++i ) {
+			s+= i==0 ? "" : ", ";
+			var elem=path[i].split( " " );
+			if( elem[0].charAt( 0 )=="#" ) {
+				s+=elem[1]+" "+elem[0];
+			}else {
+				s+=elem[1]+" ["+elem[0]+"]";
+			}
+		}
+	}
+	return s;
+}
+Test.AnotherWay._record_node_path_to_node=function( path_str, doc )
+{
+	if( path_str==null ) {
+		return null;
+	}
+	var path=path_str.split( "," );
+	var node=doc.body;
+	for( var i=0; i<path.length; ++i ) {
+		var node_i=path[i].split( " " )[0];
+		if( node_i.charAt( 0 )=="#" ) {
+			node=doc.getElementById( node_i.substring( 1 ) );
+		}else {
+			if( node_i<0 || node_i>=node.childNodes.length ) {
+				node=null;
+			}else {
+				node=node.firstChild;
+				while( node!=null ) {
+					if( node.nodeType==1 ) {  // count only HTML element nodes
+						if( node_i==0 ) {
+							break;
+						}
+						--node_i;
+					}
+					node=node.nextSibling;
+				}
+			}
+		}
+		if( node==null ) {
+			return null;
+		}
+	}
+	return node;
+}
+Test.AnotherWay._record_control_contains_id=function( s )
+{
+	return s.match( /^#record_[\w_]+/ ) && s.match( Test.AnotherWay._g_record_random_id );
+}
+Test.AnotherWay._record_checkpoint=function()
+{
+	var o={ type: "_checkpoint", time: (new Date()).getTime()-Test.AnotherWay._g_record_start_time, which: Test.AnotherWay._g_record_checkpoint_count++,
+			target: Test.AnotherWay._record_html_node_path( Test.AnotherWay._g_record_under_cursor ) };
+	Test.AnotherWay._g_record_events.push( o );
+	return o;
+}
+Test.AnotherWay._record_event=function( event )
+{
+	var unneeded=["rangeOffset","eventPhase","timeStamp","isTrusted","popupWindowFeatures","rangeOffset"];
+	if( Test.AnotherWay._g_record_started && !Test.AnotherWay._g_record_paused ) {
+		var o={};
+		for( var n in event ) {
+			var needed=!n.match( /^[A-Z0-9_]+$/ );
+			if( needed ) {
+				for( var ui=0; ui<unneeded.length; ++ui ) {
+					if( unneeded[ui]==n ) {
+						needed=false;
+						break;
+					}
+				}
+				if( needed ) {
+					var value=event[n];
+					if( typeof( value )!="object" && typeof( value )!="function" ) {
+						o[n]=value;
+					}else if( n=="target" || n=="relatedTarget" ) {
+						o[n]=Test.AnotherWay._record_html_node_path( value );
+					}
+				}
+			}
+		}
+		o["time"]=(new Date()).getTime()-Test.AnotherWay._g_record_start_time;
+		var over_record_control= o["target"]!=null && o["target"][0]!=null && Test.AnotherWay._record_control_contains_id( o["target"][0] );
+		if( !over_record_control ) {
+			Test.AnotherWay._g_record_events.push( o );
+		}
+	}
+	return true;
+}
+Test.AnotherWay._record_on_mousemove=function( event )
+{
+	var path=Test.AnotherWay._record_html_node_path( event.target );
+	var new_mouse_over_record_control= path!=null && path[0]!=null && Test.AnotherWay._record_control_contains_id( path[0] );
+	if( new_mouse_over_record_control!=Test.AnotherWay._g_record_mouse_over_record_control ) {
+		Test.AnotherWay._g_record_mouse_over_record_control=new_mouse_over_record_control;
+		Test.AnotherWay._record_control_update_ui();
+	}
+	if( event.target!=null && event.target!=Test.AnotherWay._g_record_under_cursor ) {
+		Test.AnotherWay._g_record_under_cursor=event.target;
+		var s="";
+		if( path==null || path[0]==null || !Test.AnotherWay._record_control_contains_id( path[0] ) ) {
+			s=Test.AnotherWay._record_node_path_to_string( path );
+		}
+		if( s=="" ) {
+			s="&nbsp;";
+		}
+		var cursor_over_indicator=Test.AnotherWay._record_control_get_element( "record_cursor_over" );
+		cursor_over_indicator.innerHTML=s;
+	}
+
+	var highlight_element=null;
+	if( !Test.AnotherWay._g_record_mouse_over_record_control && Test.AnotherWay._g_record_started && !Test.AnotherWay._g_record_paused ) {
+		highlight_element=event.target;
+	}
+	// highlight border disabled on recording - it causes page to scroll, issuing spurious mouseover/mouseout event
+	//Test.AnotherWay._record_control_update_highlight( highlight_element, "border" );
+
+	if( Test.AnotherWay._g_record_include_mousemove ) {
+		Test.AnotherWay._record_event( event );
+	}
+	return true;
+}
+Test.AnotherWay._record_display_checkpoint=function( o )
+{
+	var checkpoints_div=Test.AnotherWay._record_control_get_element( "record_checkpoints" );
+	var p=checkpoints_div.appendChild( checkpoints_div.ownerDocument.createElement( "div" ) );
+	p.style.marginTop="3px";
+	p.style.font="normal normal 8pt sans-serif";
+	p.style.color="#000";
+	p.style.textAligh="left";
+	p.style.position="relative";
+	p.style.width="100%";
+	var checkpoint_text="";
+	checkpoint_text+="#"+(o.which+1);
+	checkpoint_text+="  "+Test.AnotherWay._record_format_time( o.time );
+	if( o.target!=null ) {
+		checkpoint_text+=Test.AnotherWay._record_node_path_to_string( o.target );
+	}
+	p.appendChild( p.ownerDocument.createTextNode( checkpoint_text ) );
+}
+Test.AnotherWay._record_save_results=function( doc )
+{
+	// strange, but DOM-style append does not work here in opera 8.
+	var append=function( s ) { doc.write( "<div>"+s+"</div>" ); };
+	append( "/* paste this data into your javascript and pass it as an argument to replay_events method */" );
+	append( "{ checkpoints: [" );
+	var first_checkpoint=true;
+	for( var i=0; i<Test.AnotherWay._g_record_events.length; ++i ) {
+		var o=Test.AnotherWay._g_record_events[i];
+		if( o.type=="_checkpoint" ) {
+			var str= first_checkpoint ? "" : "}, ";
+			str+="function( tst, wnd ) { // #"+o.which+" time "+Test.AnotherWay._record_format_time( o.time )+" cursor was over "+Test.AnotherWay._record_node_path_to_string( o.target );
+			append( str );
+			first_checkpoint=false;
+		}
+	}
+	if( !first_checkpoint ) {
+		append( "}" );
+	}
+	append( "], events: [ " );
+	var prev_time=0;
+	for( var i=0; i<Test.AnotherWay._g_record_events.length; ++i ) {
+		var o=Test.AnotherWay._g_record_events[i];
+		var s="";
+		s+= "{";
+		var n_first=true;
+		for( var n in o ) {
+			if( n=="time" ) { // convert to relative time
+				var cur_time=o[n]-0;
+				o[n]=cur_time-prev_time;
+				prev_time=cur_time;
+			}
+			s+=n_first ? n : ", "+n;
+			s+=":";
+			if( o[n]==null ) {
+				s+="null";
+			}else {
+				s+="\""+o[n]+"\"";
+			}
+			n_first=false;
+		}
+		s+= i==Test.AnotherWay._g_record_events.length-1 ? "}" : "},";
+		append( s );
+	}
+	append( "] }" );
+	append( ";" );
+}
+
+Test.AnotherWay._g_record_border; // border highlighting element under cursor
+Test.AnotherWay._g_record_border_flashes=[]; // array of { color: color, timeout: milliseconds }
+Test.AnotherWay._g_record_border_flashing=false;
+Test.AnotherWay._g_record_border_normal_color="#d4b";
+Test.AnotherWay._record_flash_border_timeout=function()
+{
+	var color=Test.AnotherWay._g_record_border_normal_color;
+	var timeout=null;
+	if( Test.AnotherWay._g_record_border_flashes.length!=0 ) {
+		color=Test.AnotherWay._g_record_border_flashes[0].color;
+		timeout=Test.AnotherWay._g_record_border_flashes[0].timeout;
+		Test.AnotherWay._g_record_border_flashes.splice( 0, 1 );
+	}
+	if( Test.AnotherWay._g_record_border!=null ) {
+		for( var i=0; i<Test.AnotherWay._g_record_border.length; ++i ) {
+			Test.AnotherWay._g_record_border[i].style.backgroundColor=color;
+		}
+	}
+	if( timeout!=null ) {
+		setTimeout( Test.AnotherWay._record_flash_border_timeout, timeout );
+	}else {
+		Test.AnotherWay._g_record_border_flashing=false;
+	}
+}
+Test.AnotherWay._get_page_coords=function( elm )
+{
+	var point = { x: 0, y: 0 };
+	while( elm )  {
+		point.x+=elm.offsetLeft;
+		point.y+=elm.offsetTop;
+		elm=elm.offsetParent;
+	 }
+	return point;
+}
+Test.AnotherWay._set_page_coords=function( elm, x, y )
+{
+	var parent_coords={ x: 0, y: 0 };
+	if( elm.offsetParent )  {
+		parent_coords=Test.AnotherWay._get_page_coords( elm.offsetParent );
+	}
+	var new_x=x-parent_coords.x;
+	if( new_x<0 ) {
+		new_x=0;
+	}
+	elm.style.left=new_x+'px';
+	var new_y=y-parent_coords.y;
+	if( new_y<0 ) {
+		new_y=0;
+	}
+	elm.style.top=new_y+'px';
+}
+Test.AnotherWay._record_setup_highlight_positions=function( element, style, coords, positions )
+{
+	if( style=="border" ) {
+		var width=element.clientWidth;
+		var height=element.clientHeight;
+		var step=0;
+		var thickness=2;
+		var fudge_expand=4;
+		positions.push( { x: coords.x-step-thickness, y: coords.y-step-thickness, width: width+2*step+2*thickness+fudge_expand, height: thickness } );
+		positions.push( { x: coords.x+width+step+fudge_expand, y: coords.y-step-thickness, width: thickness, height: height+2*step+2*thickness+fudge_expand } );
+		positions.push( { x:positions[0].x, y:positions[0].y, width:positions[0].width, height:positions[0].height } );
+		positions.push( { x:positions[1].x, y:positions[1].y, width:positions[1].width, height:positions[1].height } );
+		positions[2].y+=height+thickness+2*step+fudge_expand;
+		positions[3].x-=width+thickness+2*step+fudge_expand;
+	}else if( style=="ball" ) {
+		positions.push( { x: coords.x+2, y: coords.y, width: 2, height: 6 } );
+		positions.push( { x: coords.x, y: coords.y+2, width: 6, height: 2 } );
+		positions.push( { x: coords.x+1, y: coords.y+1, width: 4, height: 4 } );
+	}
+}
+Test.AnotherWay._record_highlight_border=function( element, style, event ) // null - hide border
+{
+	if( element!=null ) {
+		if( Test.AnotherWay._g_record_border==null || Test.AnotherWay._g_record_border[0].ownerDocument!=element.ownerDocument ) {
+			Test.AnotherWay._g_record_border=[];
+			var n= style=="border" ? 4 : style=="ball" ? 3 : 0;
+			for( var i=0; i<4; ++i ) {
+				var b=element.ownerDocument.createElement( "div" );
+				b.style.position="absolute";
+				b.style.zIndex="1";
+				b.style.backgroundColor=Test.AnotherWay._g_record_border_normal_color;
+				element.ownerDocument.body.appendChild( b );
+				Test.AnotherWay._g_record_border.push( b );
+			}
+		}
+		var coords=null;
+		if( style=="border" ) {
+			coords=Test.AnotherWay._get_page_coords( element );
+		}else if( style=="ball" ) {
+			if( event!=null ) {
+				if( event.pageX!=null && event.pageY!=null ) {
+					coords={ x: event.pageX-0, y: event.pageY-0 };
+				}else if( event.clientX!=null && event.clientY!=null ) {
+					var doc=element.ownerDocument;
+					if( doc!=null ) {
+						coords={ x: (event.clientX-0)+doc.body.scrollLeft, y: (event.clientY-0)+doc.body.scrollTop };
+					}
+				}
+			}
+		}
+		if( coords!=null && element.clientWidth!=null && element.clientHeight!=null ) {
+			var positions=[];
+			Test.AnotherWay._record_setup_highlight_positions( element, style, coords, positions );
+			for( var i=0; i<positions.length; ++i ) {
+				var b=Test.AnotherWay._g_record_border[i];
+				var p=positions[i];
+				Test.AnotherWay._set_page_coords( b, p.x, p.y );
+				b.style.width=p.width+"px";
+				b.style.height=p.height+"px";
+				b.style.display="block";
+			}
+		}
+	}else {
+		if( Test.AnotherWay._g_record_border!=null ) {
+			for( var i=0; i<Test.AnotherWay._g_record_border.length; ++i ) {
+				Test.AnotherWay._g_record_border[i].style.display="none";
+			}
+		}
+	}
+}
+Test.AnotherWay._record_flash_border=function( color )
+{
+	if( Test.AnotherWay._g_record_border_flashing ) { //already
+		Test.AnotherWay._g_record_border_flashes.push( { color: Test.AnotherWay._g_record_border_normal_color, timeout:300 } );
+		Test.AnotherWay._g_record_border_flashes.push( { color: color, timeout:600 } );
+	}else {
+		Test.AnotherWay._g_record_border_flashing=true;
+		Test.AnotherWay._g_record_border_flashes.push( { color: color, timeout:600 } );
+		Test.AnotherWay._record_flash_border_timeout();
+	}
+}
+Test.AnotherWay._record_prepare_doc_for_results=function()
+{
+	document.open();
+	document.write( "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">" );
+	document.write( "<html><head><title> Input recording results</title>" );
+	document.write( "<style type=\"text/css\">" );
+	document.write( "body { font: normal normal smaller sans-serif; }" );
+	document.write( "div { margin-top: 3px; }" );
+	document.write( "</style></head><body>" );
+	// opera and mozilla disagree over who the opener is.
+	if( typeof( window.opener.Test )!="undefined" && typeof( window.opener.Test.AnotherWay )!="undefined" ) {
+		window.opener.Test.AnotherWay._record_save_results( document );
+		window.opener.Test.AnotherWay._g_record_waiting_for_results=false;
+		window.opener.Test.AnotherWay._record_control_update_ui();
+	}else if( typeof( window.opener.opener.Test  )!="undefined" && typeof( window.opener.opener.Test.AnotherWay )!="undefined" ) {
+		window.opener.opener.Test.AnotherWay._record_save_results( document );
+		window.opener.opener.Test.AnotherWay._g_record_waiting_for_results=false;
+		window.opener.opener.Test.AnotherWay._record_control_update_ui();
+	}
+	document.write( "</body>" );
+	document.close();
+}
+
+// global initialization
+onload=function()
+{
+	if( window.opera ) {
+		var good_opera=typeof( window.opera.version )=="function";
+		good_opera=good_opera && window.opera.version().match( /^\s*(\d+)/ );
+		good_opera=good_opera && RegExp.$1>=8;
+	}
+	var span=document.createElement( "SPAN" );
+	span.innerHTML="<!--[if IE]><br /><![endif]-"+"->";
+	var is_ie=span.getElementsByTagName( "BR" ).length>0;
+
+	Test.AnotherWay._g_test_iframe=window.frames.test_iframe;
+
+	var query_str=window.location.search;
+	if( query_str.charAt( 0 )=="?" ) {
+		query_str=query_str.substring( 1 );
+	}
+	var testlist_page="list-tests.html";
+	var auto_run=false;
+	if( query_str!="" ) {
+		var params=[query_str];
+		if( query_str.indexOf( ";" )!=-1 ) {
+			params=query_str.split( ";" );
+		}else if( query_str.indexOf( "&" )!=-1 ) {
+			params=query_str.split( "&" );
+		}
+		for( var param_i=0; param_i<params.length; ++param_i ) {
+			var param=params[param_i].split( "=" );
+			if( param[0]=="recording_results" ) {
+				if( window.opener!=null ) {
+					// we were told to show recording results - replace everything in the document with the results
+					Test.AnotherWay._record_prepare_doc_for_results();
+					return;
+				}
+			}else if( param[0]=="testpage" ) {
+				Test.AnotherWay._add_test_page_url( decodeURIComponent( param[1] ), "anotherway" );
+			}else if( param[0]=="jsantestpage" ) {
+				Test.AnotherWay._add_test_page_url( decodeURIComponent( param[1] ), "jsan" );
+			}else if( param[0]=="testlist" ) {
+				testlist_page=decodeURIComponent( param[1] );
+			}else if( param[0]=="testframe" ) {
+				if( window.opera && !good_opera ) {
+					Test.AnotherWay._show_error( "testframe parameter does not work in versions of Opera prior to 8.0. Sorry (pathches are welcome)." );
+					// Opera 7 barfs on attempt to access frame.frameElement.
+					// if someone knows a way to assign onload handler to that iframe in Opera 7
+					// without disrupting code that works in other browsers, patches are welcome.
+				}else {
+					var frame_path=param[1].split( "." );
+					var frame=top;
+					for( var frame_path_i=0; frame_path_i<frame_path.length; ++frame_path_i ) {
+						frame=frame[frame_path[frame_path_i]];
+					}
+					if( frame==null ) {
+						Test.AnotherWay._show_error( "unable to find frame specified for loading test pages: "+param[1] );
+					}else {
+						if( frame.frameElement!=null ) { // for the following assignement to onload to work, frameElement is required
+							frame=frame.frameElement;
+						}
+						Test.AnotherWay._g_test_iframe=frame;
+					}
+				}
+			}else if( param[0]=="testframe_no_clear" ) {
+				Test.AnotherWay._g_test_frame_no_clear=true;
+			}else if( param[0]=="run" ) {
+				auto_run=true;
+				if( param[1]=="all" ) {
+					Test.AnotherWay._g_pages_to_run="all";
+				}else {
+					if( Test.AnotherWay._g_pages_to_run==null || Test.AnotherWay._g_pages_to_run=="all" ) {
+						Test.AnotherWay._g_pages_to_run=[];
+					}
+					var pages=param[1].split( "," );
+					for( var i=0; i<pages.length; ++i ) {
+						Test.AnotherWay._g_pages_to_run.push( pages[i] );
+					}
+				}
+			}
+		}
+	}
+	if( Test.AnotherWay._g_test_page_urls.length==0 ) {  // if no individual pages were given on the command line, load the list
+		var result=Test.AnotherWay._set_iframe_location( window.frames["list_iframe"], testlist_page );
+		if( result.msg!=null ) {
+			Test.AnotherWay._show_error( result.msg );
+		}
+		Test.AnotherWay._g_run_on_list_load=auto_run;
+	}else {
+		Test.AnotherWay._g_run_on_main_load=auto_run;
+	}
+
+	var f=Test.AnotherWay._g_test_iframe;
+	try {
+		if( f.attachEvent!=null ) {
+			f.attachEvent( "onload", Test.AnotherWay._test_page_onload );
+		}else {
+			f.onload=Test.AnotherWay._test_page_onload;
+		}
+		if( Test.AnotherWay._g_test_iframe.nodeType!=null && Test.AnotherWay._g_test_iframe.contentWindow!=null ) { // it's iframe element, not the iframe. we need iframe.
+			Test.AnotherWay._g_test_iframe=Test.AnotherWay._g_test_iframe.contentWindow;
+		}
+	}catch(e) {
+		// ignore stupid opera error if the frame has onload handler assigned in the inline html
+	}
+	var handlers={
+		"run_all": { "onclick": Test.AnotherWay._run_all_onclick },
+		"run_selected": { "onclick": Test.AnotherWay._run_selected_onclick },
+		"unselect_all": { "onclick": Test.AnotherWay._unselect_all_onclick },
+		"record_select": { "onfocus": Test.AnotherWay._record_check_onfocus },
+		"record_input": { "onfocus": Test.AnotherWay._record_check_onfocus },
+		"record_start": { "onclick": Test.AnotherWay._record_start_onclick },
+		"clear_btn": { "onclick": Test.AnotherWay._results_clear_onclick },
+		"results_tab": { "onclick": Test.AnotherWay._tab_onclick, "onmouseover": Test.AnotherWay._tab_mouseover, "onmouseout": Test.AnotherWay._tab_mouseout },
+		"debug_tab": { "onclick": Test.AnotherWay._tab_onclick, "onmouseover": Test.AnotherWay._tab_mouseover, "onmouseout": Test.AnotherWay._tab_mouseout }
+	};
+	for( var hs in handlers ) {
+		var o=document.getElementById( hs );
+		if( o!=null ) {
+			for( var h in handlers[hs] ) {
+				o[h]=handlers[hs][h];
+			}
+		}else {
+			Test.AnotherWay._show_error( "unable to set "+h+" handler: id "+hs+" not found" );
+		}
+	}
+
+	if( window.opera && !good_opera ) {
+		Test.AnotherWay._g_no_record_msg="Input events recording and replaying is not available in opera versions prior to 8.0.";
+	}
+	if( is_ie ) {
+		Test.AnotherWay._g_no_record_msg="Input events recording and replaying is not available in internet explorer.";
+	}
+ 	if( Test.AnotherWay._g_no_record_msg!=null ) {
+		var no_record_p=document.getElementById( "record_not_supported" );
+		no_record_p.style.display="block";
+		no_record_p.appendChild( document.createTextNode( Test.AnotherWay._g_no_record_msg ) );
+	}
+
+	Test.AnotherWay._g_main_loaded=true;
+	if( Test.AnotherWay._g_run_on_main_load ) {
+		Test.AnotherWay._g_run_on_main_load=false;
+		Test.AnotherWay._run_pages_to_run();
+	}
+}
+// -->
+</script></head><body>
+
+<div id="col1">
+<div id="col1_header">Test pages:</div>
+<div id="scroller">
+<table id="testtable">
+</table>
+</div>
+<div id="run_buttons">
+<input type="button" value=" clear " id="clear_btn" />
+<input type="button" value=" run all " id="run_all" />
+<input type="button" value=" run selected " id="run_selected" />
+<input type="button" value=" unselect all " id="unselect_all" />
+</div>
+<input type="checkbox" id="dont_close_test_windows" /> do not close windows opened by tests
+<div id="error"></div>
+<div id="record_div">
+<p id="record_not_supported" style="display:none"></p>
+<p>Record mouse input for the page:</p>
+<p><input type="radio" name="record_choose" value="select" checked="checked" /> <select id="record_select"><option selected="selected">-- select a page: --</option></select></p>
+<p><input type="radio" name="record_choose" value="input" /> or enter page url: <input type="text" id="record_input" /></p>
+<p><input type="button" value=" record " id="record_start" /></p>
+</div>
+</div>
+
+<div id="col2">
+<div id="right_header">
+<span id="results_count">Results: <span id="total"></span></span>
+<span id="results_tab" class="active_tab" style="visibility:hidden">Results</span>
+<span id="debug_tab" class="inactive_tab" style="visibility:hidden">Debug</span>
+</div>
+<div id="right_frame">
+<div id="results"></div>
+<div id="debug"></div>
+</div>
+</div>
+
+<span style="display:none">
+<iframe name="list_iframe" onload="Test.AnotherWay._list_iframe_onload();"></iframe>
+<iframe name="test_iframe" onload="Test.AnotherWay._test_page_onload();"></iframe>
+
+<!-- record_control div is to be imported into other documents, so all its styles are inline -->
+-<div id="record_control" style="position:absolute;bottom:0;left:0;margin:0;padding:0.5em;width:22em;height:22em;border:1px solid;background:#ffd;font: normal normal 8pt sans-serif; color:#000; text-align: left">
+
+<p style="margin:0 0 0 0; padding:0">
+&nbsp;
+<span style="display:none;font-weight:bold;color:#408" id="record_indicator">
+recording. <span style="font-weight:normal">time: <span id="record_time"></span></span><span id="record_pause_indicator"> paused</span>
+</span>
+</p>
+
+<div id="record_cursor_over" style="margin:0;padding:2px;width:14em;height:1.1em;overflow:hidden;float:right;border:1px solid #777;background:#fff;font: normal normal 8pt sans-serif;position:relative;top:3px;color:#000;text-align:left;">&nbsp;</div>
+<p style="margin:2px 0 0 0; padding:0">
+cursor is over
+</p>
+
+<p style="margin:8px 0 0 0; padding:0;">
+ keyboard control: press
+ <span id="record_ctrl_key" style="border:1px solid #226;background:#adf;padding:0 0.5em">ctrl</span> -
+ <span id="record_shift_key" style="border:1px solid #226;background:#adf;padding:0 0.5em">shift</span> -
+</p>
+
+<p style="margin:4px 0 0 0; padding:0">
+<span id="record_s" style="border:1px solid #226;background:#adf;width:1.2em;float:left;font-weight:bold;text-align:center;margin-right:0.5em">s</span>
+<span id="record_on">to <b>start</b> recording</span>
+<span id="record_off" style="display:none">to <b>stop</b> recording</span>
+</p>
+
+<p style="margin:4px 0 0 0; padding:0">
+<span id="record_h" style="border:1px solid #226;background:#adf;width:1.2em;float:left;font-weight:bold;text-align:center;margin-right:0.5em">h</span>
+<span>to <b>hide/show</b> this window</span>
+</p>
+
+<p style="margin:4px 0 0 0; padding:0">
+<span id="record_m" style="border:1px solid #226;background:#adf;width:1.2em;float:left;font-weight:bold;text-align:center;margin-right:0.5em">m</span>
+<span id="record_include_mousemove">to <b> record</b> mousemove</span>
+<span id="record_omit_mousemove" style="display:none">to <b>omit</b> mousemove</span>
+</p>
+
+<p style="margin:4px 0 0 0; padding:0">
+<span id="record_p" style="border:1px solid #226;background:#aaa;width:1.2em;float:left;font-weight:bold;text-align:center;margin-right:0.5em">p</span>
+<span id="record_pause_on">to <b> pause</b> recording</span>
+<span id="record_pause_off" style="display:none">to <b>continue</b> recording</span>
+</p>
+
+<p style="margin:4px 0 0 0; padding:0">
+<span id="record_c" style="border:1px solid #226;background:#aaa;width:1.2em;float:left;font-weight:bold;text-align:center;margin-right:0.5em">c</span>
+<span>to add checkpoint</span>
+</p>
+
+<p style="margin:6px 0 0 0; padding:0">
+checkpoints:
+</p>
+<div id="record_checkpoints" style="position:relative;width:100%;height:6em;overflow:auto;font: normal normal 8pt sans-serif; color:#000; text-align: left">
+</div>
+</div>
+
+</span>
+</body></html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/run-tests.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Bounds.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Bounds.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Bounds.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,300 @@
+<html>
+<head>
+  <script src="../lib/OpenLayers.js"></script>
+  <script type="text/javascript"><!--
+    var bounds; 
+    function test_01_Bounds_constructor (t) {
+        t.plan( 11 );
+        bounds = new OpenLayers.Bounds(0,2,10,4);
+        t.ok( bounds instanceof OpenLayers.Bounds, "new OpenLayers.Bounds returns Bounds object" );
+        t.eq( bounds.CLASS_NAME, "OpenLayers.Bounds", "bounds.CLASS_NAME is set correctly" );
+        t.eq( bounds.left, 0, "bounds.left is set correctly" );
+        t.eq( bounds.bottom, 2, "bounds.bottom is set correctly" );
+        t.eq( bounds.right, 10, "bounds.right is set correctly" );
+        t.eq( bounds.top, 4, "bounds.top is set correctly" );
+        t.eq( bounds.getWidth(), 10, "bounds.getWidth() returns correct value" );
+        t.eq( bounds.getHeight(), 2, "bounds.getHeight() returns correct value" );
+        
+        var sz = bounds.getSize();
+        var size = new OpenLayers.Size(10,2);
+        t.ok(sz.equals(size),"bounds.getSize() has correct value" );
+    
+        var center = new OpenLayers.Pixel(5,3);
+        var boundsCenter = bounds.getCenterPixel();
+        t.ok( boundsCenter.equals(center), "bounds.getCenterLonLat() has correct value" );
+
+        var center = new OpenLayers.LonLat(5,3);
+        var boundsCenter = bounds.getCenterLonLat();
+        t.ok( boundsCenter.equals(center), "bounds.getCenterLonLat() has correct value" );
+     }
+
+    function test_01a_Bounds_constructorFromStrings(t) {
+        t.plan( 6 );
+        bounds = new OpenLayers.Bounds("0","2","10","4");
+        t.ok( bounds instanceof OpenLayers.Bounds, "new OpenLayers.Bounds returns Bounds object" );
+        t.eq( bounds.CLASS_NAME, "OpenLayers.Bounds", "bounds.CLASS_NAME is set correctly" );
+        t.eq( bounds.left, 0, "bounds.left is set correctly" );
+        t.eq( bounds.bottom, 2, "bounds.bottom is set correctly" );
+        t.eq( bounds.right, 10, "bounds.right is set correctly" );
+        t.eq( bounds.top, 4, "bounds.top is set correctly" );
+        
+    }
+
+     function test_02_Bounds_toBBOX(t) {
+         t.plan( 5 );
+         bounds = new OpenLayers.Bounds(1,2,3,4);
+         t.eq( bounds.toBBOX(), "1,2,3,4", "toBBOX() returns correct value." );
+         bounds = new OpenLayers.Bounds(1.00000001,2,3,4);
+         t.eq( bounds.toBBOX(), "1,2,3,4", "toBBOX() rounds off small differences." );
+         bounds = new OpenLayers.Bounds(1.00000001,2.5,3,4);
+         t.eq( bounds.toBBOX(), "1,2.5,3,4", "toBBOX() returns correct value. for a half number" );
+         bounds = new OpenLayers.Bounds(1,2.5555555,3,4);
+         t.eq( bounds.toBBOX(), "1,2.555556,3,4", "toBBOX() rounds to correct value." );
+         bounds = new OpenLayers.Bounds(1,2.5555555,3,4);
+         t.eq( bounds.toBBOX(1), "1,2.6,3,4", "toBBOX() rounds to correct value with power provided." );
+         bounds = new OpenLayers.Bounds(1,2.5555555,3,4);
+     }
+
+     function test_03_Bounds_toString(t) {
+         t.plan( 1 );
+         bounds = new OpenLayers.Bounds(1,2,3,4);
+         t.eq( bounds.toString(), "left-bottom=(1,2) right-top=(3,4)", "toString() returns correct value." ); 
+     }
+
+     function test_04_Bounds_contains(t) {
+         t.plan( 6 );
+         bounds = new OpenLayers.Bounds(10,10,40,40);
+         t.eq( bounds.contains(20,20), true, "bounds(10,10,40,40) correctly contains LonLat(20,20)" );
+         t.eq( bounds.contains(0,0), false, "bounds(10,10,40,40) correctly does not contain LonLat(0,0)" );
+         t.eq( bounds.contains(40,40), true, "bounds(10,10,40,40) correctly contains LonLat(40,40) with inclusive set to true" );
+         t.eq( bounds.contains(40,40, false), false, "bounds(10,10,40,40) correctly does not contain LonLat(40,40) with inclusive set to false" );
+
+         var px = new OpenLayers.Pixel(15,30);
+         t.eq( bounds.containsPixel(px), bounds.contains(px.x, px.y), "containsPixel works");
+
+         var ll = new OpenLayers.LonLat(15,30);
+         t.eq( bounds.containsLonLat(ll), bounds.contains(ll.lon, ll.lat), "containsLonLat works");
+
+     }
+
+     function test_05_Bounds_fromString(t) {
+        t.plan( 10 );
+        bounds = OpenLayers.Bounds.fromString("1,2,3,4");
+        t.ok( bounds instanceof OpenLayers.Bounds, "new OpenLayers.Bounds returns Bounds object" );
+        t.eq( bounds.left, 1, "bounds.left is set correctly" );
+        t.eq( bounds.bottom, 2, "bounds.bottom is set correctly" );
+        t.eq( bounds.right, 3, "bounds.right is set correctly" );
+        t.eq( bounds.top, 4, "bounds.top is set correctly" );
+
+        bounds = OpenLayers.Bounds.fromString("1.1,2.2,3.3,4.4");
+        t.ok( bounds instanceof OpenLayers.Bounds, "new OpenLayers.Bounds returns Bounds object" );
+        t.eq( bounds.left, 1.1, "bounds.left is set correctly" );
+        t.eq( bounds.bottom, 2.2, "bounds.bottom is set correctly" );
+        t.eq( bounds.right, 3.3, "bounds.right is set correctly" );
+        t.eq( bounds.top, 4.4, "bounds.top is set correctly" );
+
+     }
+
+     function test_06_Bounds_getSize(t) {
+         t.plan( 1 );
+         var bounds = new OpenLayers.Bounds(0,10,100,120);
+
+         t.ok( bounds.getSize().equals(new OpenLayers.Size(100, 110)), "getCenterPixel() works correctly");
+     }
+     
+     function test_07_Bounds_clone(t) {
+        t.plan( 6 );
+        var oldBounds = new OpenLayers.Bounds(1,2,3,4);
+        var bounds = oldBounds.clone();
+        t.ok( bounds instanceof OpenLayers.Bounds, "clone returns new OpenLayers.Bounds object" );
+        t.eq( bounds.left, 1, "bounds.left is set correctly" );
+        t.eq( bounds.bottom, 2, "bounds.bottom is set correctly" );
+        t.eq( bounds.right, 3, "bounds.right is set correctly" );
+        t.eq( bounds.top, 4, "bounds.top is set correctly" );
+        
+        oldBounds.left = 100;
+        t.eq( bounds.left, 1, "changing olBounds.left does not change bounds.left" );
+     }
+
+     function test_08a_Bounds_intersectsBounds(t) {
+         t.plan( 17 );
+
+        var aBounds = new OpenLayers.Bounds(-180, -90, 180, 90);
+
+        //inside
+        var bBounds = new OpenLayers.Bounds(-20, -10, 20, 10);
+        var cBounds = new OpenLayers.Bounds(-181,-90,180,90);
+        t.eq( aBounds.intersectsBounds(bBounds),        true, "(" + aBounds.toBBOX() + ") correctly intersects (" + bBounds.toBBOX() + ")" );
+        t.eq( aBounds.intersectsBounds(bBounds, true),  true, "(" + aBounds.toBBOX() + ") correctly intersects (" + bBounds.toBBOX() + "), inclusive is true" );
+        t.eq( aBounds.intersectsBounds(bBounds, false), true, "(" + aBounds.toBBOX() + ") correctly intersects (" + bBounds.toBBOX() + "), inclusive is false" );
+        t.eq( aBounds.intersectsBounds(cBounds, false), true, "aBounds with cBounds adjusted one degree left passes intersect bounds. (3 sides match, 1 side different)." );
+        t.eq( cBounds.intersectsBounds(aBounds, false), true, "cBounds with aBounds adjusted one degree left passes intersect bounds. (3 sides match, 1 side different)." );
+
+        //outside
+        bBounds = new OpenLayers.Bounds(-181, -91, 181, 91);
+        t.eq( aBounds.intersectsBounds(bBounds),        true, "(" + aBounds.toBBOX() + ") correctly intersects (" + bBounds.toBBOX() + ")" );
+        t.eq( aBounds.intersectsBounds(bBounds, true),  true, "(" + aBounds.toBBOX() + ") correctly intersects (" + bBounds.toBBOX() + "), inclusive is true" );
+        t.eq( aBounds.intersectsBounds(bBounds, false), true, "(" + aBounds.toBBOX() + ") correctly intersects (" + bBounds.toBBOX() + "), inclusive is false" );
+
+        //total intersect
+        bBounds = new OpenLayers.Bounds(-185, -100, 20, 50);
+        t.eq( aBounds.intersectsBounds(bBounds),        true, "(" + aBounds.toBBOX() + ") correctly intersects (" + bBounds.toBBOX() + ")" );
+        t.eq( aBounds.intersectsBounds(bBounds, true),  true, "(" + aBounds.toBBOX() + ") correctly intersects (" + bBounds.toBBOX() + "), inclusive is true" );
+        t.eq( aBounds.intersectsBounds(bBounds, false), true, "(" + aBounds.toBBOX() + ") correctly intersects (" + bBounds.toBBOX() + "), inclusive is false" );
+
+        //border intersect
+        bBounds = new OpenLayers.Bounds(-360, -180, -180, -90);
+        t.eq( aBounds.intersectsBounds(bBounds),        true, "(" + aBounds.toBBOX() + ") correctly intersects (" + bBounds.toBBOX() + ")" );
+        t.eq( aBounds.intersectsBounds(bBounds, true),  true, "(" + aBounds.toBBOX() + ") correctly intersects (" + bBounds.toBBOX() + "), inclusive is true" );
+        t.eq( aBounds.intersectsBounds(bBounds, false), false, "(" + aBounds.toBBOX() + ") does not intersect (" + bBounds.toBBOX() + "), inclusive is false" );
+
+        //no intersect
+        bBounds = new OpenLayers.Bounds(-360, -180, -185, -95);
+        t.eq( aBounds.intersectsBounds(bBounds),        false, "(" + aBounds.toBBOX() + ") does not intersect (" + bBounds.toBBOX() + ")" );
+        t.eq( aBounds.intersectsBounds(bBounds, true),  false, "(" + aBounds.toBBOX() + ") does not intersect (" + bBounds.toBBOX() + "), inclusive is true" );
+        t.eq( aBounds.intersectsBounds(bBounds, false), false, "(" + aBounds.toBBOX() + ") does not intersect (" + bBounds.toBBOX() + "), inclusive is false" );
+
+     }
+     
+     function test_08b_Bounds_containsBounds(t) {
+         t.plan( 35 );
+         containerBounds = new OpenLayers.Bounds(10,10,40,40);
+
+         //totally outside
+         bounds = new OpenLayers.Bounds(0,0,5,5);
+         t.eq( containerBounds.containsBounds(bounds)              , false, "(" + containerBounds.toBBOX() + ") correctly does not contain (" + bounds.toBBOX() + ")");
+         t.eq( containerBounds.containsBounds(bounds, false)       , false, "(" + containerBounds.toBBOX() + ") correctly does not contain (" + bounds.toBBOX() + ") when partial is false" );
+         t.eq( containerBounds.containsBounds(bounds, false, true) , false, "(" + containerBounds.toBBOX() + ") correctly does not contain (" + bounds.toBBOX() + ") when partial is false, inclusive is true" );
+         t.eq( containerBounds.containsBounds(bounds, false, false), false, "(" + containerBounds.toBBOX() + ") correctly does not contain (" + bounds.toBBOX() + ") when partial is false, inclusive is false" );
+         t.eq( containerBounds.containsBounds(bounds, true)        , false , "(" + containerBounds.toBBOX() + ") correctly does not contain (" + bounds.toBBOX() + ") when partial is true" );
+         t.eq( containerBounds.containsBounds(bounds, true, true)  , false, "(" + containerBounds.toBBOX() + ") correctly does not contain (" + bounds.toBBOX() + ") when partial is true, inclusive is true" );
+         t.eq( containerBounds.containsBounds(bounds, true, false) , false, "(" + containerBounds.toBBOX() + ") correctly does not contain (" + bounds.toBBOX() + ") when partial is true, inclusive is false" );
+
+         //totally outside on border
+         bounds = new OpenLayers.Bounds(15,0,30,10);
+         t.eq( containerBounds.containsBounds(bounds)              , false, "(" + containerBounds.toBBOX() + ") correctly does not contain (" + bounds.toBBOX() + ")");
+         t.eq( containerBounds.containsBounds(bounds, false)       , false, "(" + containerBounds.toBBOX() + ") correctly does not contain (" + bounds.toBBOX() + ") when partial is false" );
+         t.eq( containerBounds.containsBounds(bounds, false, true) , false, "(" + containerBounds.toBBOX() + ") correctly does not contain (" + bounds.toBBOX() + ") when partial is false, inclusive is true" );
+         t.eq( containerBounds.containsBounds(bounds, false, false), false, "(" + containerBounds.toBBOX() + ") correctly does not contain (" + bounds.toBBOX() + ") when partial is false, inclusive is false" );
+         t.eq( containerBounds.containsBounds(bounds, true)        , true, "(" + containerBounds.toBBOX() + ") correctly contains (" + bounds.toBBOX() + ") when partial is true" );
+         t.eq( containerBounds.containsBounds(bounds, true, true)  , true, "(" + containerBounds.toBBOX() + ") correctly contains (" + bounds.toBBOX() + ") when partial is true, inclusive is true" );
+         t.eq( containerBounds.containsBounds(bounds, true, false) , false, "(" + containerBounds.toBBOX() + ") correctly does not contain (" + bounds.toBBOX() + ") when partial is true, inclusive is false" );
+
+         //partially inside
+         bounds = new OpenLayers.Bounds(20,20,50,30);
+         t.eq( containerBounds.containsBounds(bounds)              , false, "(" + containerBounds.toBBOX() + ") correctly does not contain (" + bounds.toBBOX() + ")");
+         t.eq( containerBounds.containsBounds(bounds, false)       , false, "(" + containerBounds.toBBOX() + ") correctly does not contain (" + bounds.toBBOX() + ") when partial is false" );
+         t.eq( containerBounds.containsBounds(bounds, false, true) , false, "(" + containerBounds.toBBOX() + ") correctly does not contain (" + bounds.toBBOX() + ") when partial is false, inclusive is true" );
+         t.eq( containerBounds.containsBounds(bounds, false, false), false, "(" + containerBounds.toBBOX() + ") correctly does not contain (" + bounds.toBBOX() + ") when partial is false, inclusive is false" );
+         t.eq( containerBounds.containsBounds(bounds, true)        , true, "(" + containerBounds.toBBOX() + ") correctly contains (" + bounds.toBBOX() + ") when partial is true" );
+         t.eq( containerBounds.containsBounds(bounds, true, true)  , true, "(" + containerBounds.toBBOX() + ") correctly contains (" + bounds.toBBOX() + ") when partial is true, inclusive is true" );
+         t.eq( containerBounds.containsBounds(bounds, true, false) , true, "(" + containerBounds.toBBOX() + ") correctly contains (" + bounds.toBBOX() + ") when partial is true, inclusive is false" );
+
+         //totally inside on border
+         bounds = new OpenLayers.Bounds(10,20,30,30);
+         t.eq( containerBounds.containsBounds(bounds)              , true, "(" + containerBounds.toBBOX() + ") correctly contains (" + bounds.toBBOX() + ")");
+         t.eq( containerBounds.containsBounds(bounds, false)       , true, "(" + containerBounds.toBBOX() + ") correctly contains (" + bounds.toBBOX() + ") when partial is false" );
+         t.eq( containerBounds.containsBounds(bounds, false, true) , true, "(" + containerBounds.toBBOX() + ") correctly contains (" + bounds.toBBOX() + ") when partial is false, inclusive is true" );
+         t.eq( containerBounds.containsBounds(bounds, false, false), false, "(" + containerBounds.toBBOX() + ") correctly does not contain (" + bounds.toBBOX() + ") when partial is false, inclusive is false" );
+         t.eq( containerBounds.containsBounds(bounds, true)        , true, "(" + containerBounds.toBBOX() + ") correctly contains (" + bounds.toBBOX() + ") when partial is true" );
+         t.eq( containerBounds.containsBounds(bounds, true, true)  , true, "(" + containerBounds.toBBOX() + ") correctly contains (" + bounds.toBBOX() + ") when partial is true, inclusive is true" );
+         t.eq( containerBounds.containsBounds(bounds, true, false) , true, "(" + containerBounds.toBBOX() + ") correctly contains (" + bounds.toBBOX() + ") when partial is true, inclusive is false" );
+
+         //totally inside
+         bounds = new OpenLayers.Bounds(20,20,30,30);
+         t.eq( containerBounds.containsBounds(bounds)              , true, "(" + containerBounds.toBBOX() + ") correctly contains (" + bounds.toBBOX() + ")");
+         t.eq( containerBounds.containsBounds(bounds, false)       , true, "(" + containerBounds.toBBOX() + ") correctly contains (" + bounds.toBBOX() + ") when partial is false" );
+         t.eq( containerBounds.containsBounds(bounds, false, true) , true, "(" + containerBounds.toBBOX() + ") correctly contains (" + bounds.toBBOX() + ") when partial is false, inclusive is true" );
+         t.eq( containerBounds.containsBounds(bounds, false, false), true, "(" + containerBounds.toBBOX() + ") correctly contains (" + bounds.toBBOX() + ") when partial is false, inclusive is false" );
+         t.eq( containerBounds.containsBounds(bounds, true)        , true, "(" + containerBounds.toBBOX() + ") correctly contains (" + bounds.toBBOX() + ") when partial is true" );
+         t.eq( containerBounds.containsBounds(bounds, true, true)  , true, "(" + containerBounds.toBBOX() + ") correctly contains (" + bounds.toBBOX() + ") when partial is true, inclusive is true" );
+         t.eq( containerBounds.containsBounds(bounds, true, false) , true, "(" + containerBounds.toBBOX() + ") correctly contains (" + bounds.toBBOX() + ") when partial is true, inclusive is false" );
+     }
+     
+     function test_09_Bounds_determineQuadrant(t) {
+
+        t.plan( 4 );
+        var bounds = new OpenLayers.Bounds(0,0,100,100);
+
+        var tl = new OpenLayers.LonLat(25, 75);
+        var tr = new OpenLayers.LonLat(75, 75);
+        var bl = new OpenLayers.LonLat(25, 25);
+        var br = new OpenLayers.LonLat(75, 25);
+
+        t.eq( bounds.determineQuadrant(tl), "tl", "bounds.determineQuadrant correctly identifies a coordinate in the top left quadrant");
+        t.eq( bounds.determineQuadrant(tr), "tr", "bounds.determineQuadrant correctly identifies a coordinate in the top right quadrant");
+        t.eq( bounds.determineQuadrant(bl), "bl", "bounds.determineQuadrant correctly identifies a coordinate in the bottom left quadrant");
+        t.eq( bounds.determineQuadrant(br), "br", "bounds.determineQuadrant correctly identifies a coordinate in the bottom right quadrant");
+     }
+
+     function test_10_Bounds_oppositeQuadrant(t) {
+
+        t.plan( 4 );
+
+        t.eq( OpenLayers.Bounds.oppositeQuadrant("tl"), "br", "OpenLayers.Bounds.oppositeQuadrant returns 'br' for 'tl'");
+        t.eq( OpenLayers.Bounds.oppositeQuadrant("tr"), "bl", "OpenLayers.Bounds.oppositeQuadrant returns 'bl' for 'tr'");
+        t.eq( OpenLayers.Bounds.oppositeQuadrant("bl"), "tr", "OpenLayers.Bounds.oppositeQuadrant returns 'tr' for 'bl'");
+        t.eq( OpenLayers.Bounds.oppositeQuadrant("br"), "tl", "OpenLayers.Bounds.oppositeQuadrant returns 'tl' for 'br'");
+     }
+
+     function test_11_Bounds_equals(t) {
+         t.plan( 3 );
+         var boundsA = new OpenLayers.Bounds(1,2,3,4);
+         var boundsB = new OpenLayers.Bounds(1,2,3,4);
+         var boundsC = new OpenLayers.Bounds(1,5,3,4);
+         
+         t.ok( boundsA.equals(boundsB), "equals() returns true on two equal bounds." );
+         t.ok( !boundsA.equals(boundsC), "equals() returns false on two different bounds." );
+         t.ok( !boundsA.equals(null), "equals() returns false on comparison to null");
+     }
+
+     function test_12_Bounds_getHeight_getWidth(t) {
+         t.plan( 2 );
+         var bounds = new OpenLayers.Bounds(10,20,100,120);
+
+         t.eq( bounds.getWidth(), 90, "getWidth() works" );
+         t.eq( bounds.getHeight(), 100, "getHeight() works" );
+
+     }
+
+     function test_13_Bounds_getCenters(t) {
+         t.plan( 2 );
+         var bounds = new OpenLayers.Bounds(0,20,100,120);
+
+                 t.ok( bounds.getCenterPixel().equals(new OpenLayers.Pixel(50, 70)), "getCenterPixel() works correctly");
+         t.ok( bounds.getCenterLonLat().equals(new OpenLayers.LonLat(50, 70)), "getCenterLonLat() works correctly");
+     }
+
+     function test_14_Bounds_fromArray(t) {
+        t.plan( 5 );
+        
+        var bbox = [1,2,3,4];
+        bounds = OpenLayers.Bounds.fromArray(bbox);
+        t.ok( bounds instanceof OpenLayers.Bounds, "new OpenLayers.Bounds returns Bounds object" );
+        t.eq( bounds.left, 1, "bounds.left is set correctly" );
+        t.eq( bounds.bottom, 2, "bounds.bottom is set correctly" );
+        t.eq( bounds.right, 3, "bounds.right is set correctly" );
+        t.eq( bounds.top, 4, "bounds.top is set correctly" );
+     }
+
+     function test_14_Bounds_fromSize(t) {
+        t.plan( 5 );
+        
+        var height = 15;
+        var width = 16;
+        var size = new OpenLayers.Size(width, height);
+        bounds = OpenLayers.Bounds.fromSize(size);
+        t.ok( bounds instanceof OpenLayers.Bounds, "new OpenLayers.Bounds returns Bounds object" );
+        t.eq( bounds.left, 0, "bounds.left is set correctly" );
+        t.eq( bounds.bottom, height, "bounds.bottom is set correctly" );
+        t.eq( bounds.right, width, "bounds.right is set correctly" );
+        t.eq( bounds.top, 0, "bounds.top is set correctly" );
+     }
+
+  // -->
+  </script>
+</head>
+<body>
+</body>
+</html>
+


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Bounds.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Class.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Class.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Class.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,106 @@
+<html>
+<head>
+  <script src="../lib/OpenLayers.js"></script>
+  <script type="text/javascript"><!--
+    var isMozilla = (navigator.userAgent.indexOf("compatible") == -1);
+    
+    function test_01_Class_create (t) {
+        t.plan( 3 );
+        var cls = OpenLayers.Class.create();
+        cls.prototype = {
+            initialize: function () {
+                if (isMozilla)
+                    t.ok(this instanceof cls,
+                                "initialize is called on the right class");
+                else
+                    t.ok(true, "initialize is called");
+            }
+        };
+        var obj = new cls();
+        t.eq(typeof obj, "object", "obj is an object");
+        if (isMozilla)
+            t.ok(obj instanceof cls,
+                        "object is of the right class");
+        else
+            t.ok(true, "this test doesn't work in IE");
+    }
+
+    function test_02_Class_inherit (t) {
+        t.plan( 20 );
+        var A = OpenLayers.Class.create();
+        var initA = 0;
+        A.prototype = {
+            count: 0,
+            initialize: function () {
+                initA++;
+                this.count++;
+            }
+        };
+
+        var B = OpenLayers.Class.create();
+        var initB = 0;
+        B.prototype = OpenLayers.Class.inherit( A, {
+            initialize: function () {
+                A.prototype.initialize.apply(this, arguments);
+                initB++;
+                this.count++;
+            }
+        });
+
+        var mixin = OpenLayers.Class.create()
+        mixin.prototype = {
+            mixed: true
+        };
+
+        t.eq( initA, 0, "class A not inited" );
+        t.eq( initB, 0, "class B not inited" );
+
+        var objA = new A();
+        t.eq( objA.count, 1, "object A init" );
+        t.eq( initA, 1, "class A init" );
+        if (isMozilla) 
+            t.ok( objA instanceof A, "obj A isa A" );
+        else
+            t.ok( true, "IE sucks" );
+
+        var objB = new B();
+        t.eq( initA, 2, "class A init" );
+        t.eq( initB, 1, "class B init" );
+        t.eq( objB.count, 2, "object B init twice" );
+        if (isMozilla) {
+            t.ok( objB instanceof A, "obj B isa A" );
+            t.ok( objB instanceof B, "obj B isa B" );
+        } else {
+            t.ok( true, "IE sucks" );
+            t.ok( true, "IE sucks" );
+        }
+
+        var C = OpenLayers.Class.create();
+        C.prototype = OpenLayers.Class.inherit( B, mixin, {count: 0} );
+        t.eq( initA, 2, "class A init unchanged" );
+        t.eq( initB, 1, "class B init unchanged" );
+        
+        var objC = new C();
+        t.eq( initA, 3, "class A init changed" );
+        t.eq( initB, 2, "class B init changed" );
+        t.eq( objC.count, 2, "object C init changed" );
+        if (isMozilla) {
+            t.ok( objC instanceof A, "obj C isa A" );
+            t.ok( objC instanceof B, "obj C isa B" );
+            t.ok( objC instanceof C, "obj C isa C" );
+            t.ok( !(objC instanceof mixin), "obj C isn'ta mixin" );
+        } else {
+            t.ok( true, "IE sucks" );
+            t.ok( true, "IE sucks" );
+            t.ok( true, "IE sucks" );
+            t.ok( true, "IE sucks" );
+        }
+        t.eq( objC.mixed, true, "class C mixes has mixin properties" );
+    }
+
+  // -->
+  </script>
+</head>
+<body>
+</body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Class.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Control.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Control.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Control.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,18 @@
+<html>
+<head>
+  <script src="../lib/OpenLayers.js"></script>
+  <script type="text/javascript"><!--
+    var map; 
+    function test_01_Control_constructor (t) {
+        t.plan( 1 );
+    
+        control = new OpenLayers.Control();
+        t.ok( control instanceof OpenLayers.Control, "new OpenLayers.Control returns object" );
+    }
+  // -->
+  </script>
+</head>
+<body>
+    <div id="map" style="width: 1024px; height: 512px;"/>
+</body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Control.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Control_LayerSwitcher.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Control_LayerSwitcher.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Control_LayerSwitcher.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,114 @@
+<html>
+<head>
+  <script src="../lib/OpenLayers.js"></script>
+  <script type="text/javascript"><!--
+    var map; 
+
+    function test_01_Control_LayerSwitcher_constructor (t) {
+        t.plan( 1 );
+    
+        control = new OpenLayers.Control.LayerSwitcher();
+        t.ok( control instanceof OpenLayers.Control.LayerSwitcher, "new OpenLayers.Control.LayerSwitcher returns object" );
+    }
+
+    function test_02_Control_LayerSwitcher_draw (t) {
+        t.plan( 2 );
+
+        map = new OpenLayers.Map('map');
+        control = new OpenLayers.Control.LayerSwitcher();
+        map.addControl(control);
+
+        var div = control.draw();
+        t.ok( control.div != null, "draw makes a div" );
+        t.ok( div != null, "draw returns its div" );
+    }
+ 
+    function test_03_Control_LayerSwitcher_loadContents(t) {
+
+        t.plan( 5 );
+
+        map = new OpenLayers.Map('map');
+        var layer = new OpenLayers.Layer.WMS("WMS", 
+            "http://octo.metacarta.com/cgi-bin/mapserv?",
+            {map: "/mapdata/vmap_wms.map", layers: "basic"});
+        map.addLayer(layer);
+
+        markers = new OpenLayers.Layer.Markers("markers");
+        map.addLayer(markers);
+
+        control = new OpenLayers.Control.LayerSwitcher();
+        map.addControl(control);
+
+        t.ok(control.layersDiv != null, "correctly makes layers div");
+        t.ok(control.baseLayersDiv != null, "correctly makes layers div");
+        t.ok(control.dataLayersDiv != null, "correctly makes layers div");
+
+        t.ok(control.maximizeDiv != null, "correctly makes resize div");
+        t.ok(control.minimizeDiv != null, "correctly makes resize div");
+
+        
+
+    }
+
+
+    function test_04_Control_LayerSwitcher_redraw (t) {
+
+        t.plan( 8 );
+
+        map = new OpenLayers.Map('map');
+        var layer = new OpenLayers.Layer.WMS("WMS", 
+            "http://octo.metacarta.com/cgi-bin/mapserv?",
+            {map: "/mapdata/vmap_wms.map", layers: "basic"});
+        map.addLayer(layer);
+
+        markers = new OpenLayers.Layer.Markers("markers");
+        map.addLayer(markers);
+
+        control = new OpenLayers.Control.LayerSwitcher();
+        map.addControl(control);
+
+        
+        var wmsInput = $("input_" + layer.name);
+        t.ok(wmsInput != null, "correctly makes an input for wms layer");
+        t.eq(wmsInput.type, "radio", "wms correctly made a radio button");
+        t.eq(wmsInput.name, "baseLayers", "wms correctly named");
+        t.eq(wmsInput.value, layer.name, "wms correctly valued");
+        
+        var markersInput = $("input_" + markers.name);
+        t.ok(markersInput != null, "correctly makes an input for markers layer");
+        t.eq(markersInput.type, "checkbox", "wms correctly made a radio button");
+        t.eq(markersInput.name, markers.name, "wms correctly named");
+        t.eq(markersInput.value, markers.name, "wms correctly valued");
+
+    }
+    function test_05_Control_LayerSwitcher_ascendingw (t) {
+
+        t.plan( 4 );
+
+        map = new OpenLayers.Map('map');
+        var layer = new OpenLayers.Layer.WMS("WMS", 
+            "http://octo.metacarta.com/cgi-bin/mapserv?",
+            {map: "/mapdata/vmap_wms.map", layers: "basic"});
+        map.addLayer(layer);
+
+        markers = new OpenLayers.Layer.Markers("markers");
+        map.addLayer(markers);
+
+        control = new OpenLayers.Control.LayerSwitcher();
+        map.addControl(control);
+        control2 = new OpenLayers.Control.LayerSwitcher({'ascending':false});
+        map.addControl(control2);
+        t.ok(control.div.childNodes[1].childNodes[0].innerHTML.match("Base Layer"), "Base Layers first in LayerSwitcher with ascending true");
+        t.ok(control.div.childNodes[1].childNodes[2].innerHTML.match("Overlays"), "Overlays in LayerSwitcher with ascending true");
+        t.ok(control2.div.childNodes[1].childNodes[2].innerHTML.match("Base Layer"), "Base Layers last in LayerSwitcher with ascending false");
+        t.ok(control2.div.childNodes[1].childNodes[0].innerHTML.match("Overlays"), "Base Layers last in LayerSwitcher with ascending false");
+    }
+
+
+  // -->
+  </script>
+</head>
+<body>
+    <div id="map" style="width: 1024px; height: 512px;"/>
+</body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Control_LayerSwitcher.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Control_MouseToolbar.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Control_MouseToolbar.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Control_MouseToolbar.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,58 @@
+<html>
+<head>
+  <script src="../lib/OpenLayers.js"></script>
+  <script type="text/javascript"><!--
+    var map; 
+    function test_01_Control_MouseToolbar_constructor (t) {
+        t.plan( 1 );
+    
+        control = new OpenLayers.Control.MouseToolbar();
+        t.ok( control instanceof OpenLayers.Control.MouseToolbar, "new OpenLayers.Control.MouseToolbar returns object" );
+    }
+    function test_02_Control_MouseToolbar_addControl (t) {
+        t.plan( 7 );
+        map = new OpenLayers.Map('map');
+        control = new OpenLayers.Control.MouseToolbar();
+        t.ok( control instanceof OpenLayers.Control.MouseToolbar, "new OpenLayers.Control.MouseToolbar returns object" );
+        t.ok( map instanceof OpenLayers.Map, "new OpenLayers.Map creates map" );
+        map.addControl(control);
+        t.ok( control.map === map, "Control.map is set to the map object" );
+        t.ok( map.controls[3] === control, "map.controls contains control" );
+        t.eq( parseInt(control.div.style.zIndex), map.Z_INDEX_BASE['Control'] + 4, "Control div zIndexed properly" );
+        t.eq( parseInt(map.viewPortDiv.lastChild.style.zIndex), map.Z_INDEX_BASE['Control'] + 4, "Viewport div contains control div" );
+        t.eq( control.div.style.top, "6px", "Control div top located correctly by default");
+
+    }
+    function test_03_Control_MouseToolbar_control_events (t) {
+        t.plan( 1 );
+        if ((navigator.userAgent.indexOf("compatible") == -1)) { 
+            var evt = {which: 1}; // control expects left-click
+            map = new OpenLayers.Map('map');
+            var layer = new OpenLayers.Layer.WMS("Test Layer", 
+                "http://octo.metacarta.com/cgi-bin/mapserv?",
+                {map: "/mapdata/vmap_wms.map", layers: "basic"});
+            map.addLayer(layer);
+
+            control = new OpenLayers.Control.MouseToolbar();
+            map.addControl(control);
+
+            var centerLL = new OpenLayers.LonLat(0,0); 
+            map.setCenter(centerLL, 5);
+
+            evt.shiftKey = true;
+            evt.xy = new OpenLayers.Size(5,5);
+            control.defaultMouseDown(evt);
+            evt.xy = new OpenLayers.Size(15,15);
+            control.defaultMouseUp(evt);
+            t.eq(map.getZoom(), 6, "Map zoom set correctly after zoombox");
+        } else {
+            t.ok(true, "IE does not run this test.") 
+        }
+    }
+  // -->
+  </script>
+</head>
+<body>
+    <div id="map" style="width: 1024px; height: 512px;"/>
+</body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Control_MouseToolbar.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Control_OverviewMap.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Control_OverviewMap.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Control_OverviewMap.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,75 @@
+<html>
+<head>
+  <script src="../lib/OpenLayers.js"></script>
+  <script type="text/javascript"><!--
+    var map; 
+    function test_01_Control_PanZoom_constructor (t) {
+        t.plan( 1 );
+    
+        control = new OpenLayers.Control.OverviewMap();
+        t.ok( control instanceof OpenLayers.Control.OverviewMap, "new OpenLayers.Control.OverviewMap returns object" );
+    }
+    function test_02_Control_PanZoom_addControl (t) {
+        t.plan( 6 );
+        map = new OpenLayers.Map('map');
+        control = new OpenLayers.Control.OverviewMap();
+        t.ok( control instanceof OpenLayers.Control.OverviewMap, "new OpenLayers.Control.OverviewMap returns object" );
+        t.ok( map instanceof OpenLayers.Map, "new OpenLayers.Map creates map" );
+        map.addControl(control);
+        t.ok( control.map === map, "Control.map is set to the map object" );
+        t.ok( map.controls[3] === control, "map.controls contains control" );
+        t.eq( parseInt(control.div.style.zIndex), map.Z_INDEX_BASE['Control'] + 4, "Control div zIndexed properly" );
+        t.eq( parseInt(map.viewPortDiv.lastChild.style.zIndex), map.Z_INDEX_BASE['Control'] + 4, "Viewport div contains control div" );
+
+    }
+    function test_03_Control_PanZoom_control_events (t) {
+        t.plan( 10 );
+        var evt = {which: 1}; // control expects left-click
+        map = new OpenLayers.Map('map');
+        var layer = new OpenLayers.Layer.WMS("Test Layer", 
+            "http://octo.metacarta.com/cgi-bin/mapserv?",
+            {map: "/mapdata/vmap_wms.map", layers: "basic"});
+        map.addLayer(layer);
+
+        control = new OpenLayers.Control.OverviewMap();
+        map.addControl(control, new OpenLayers.Pixel(20,20));
+
+        var centerLL = new OpenLayers.LonLat(-71,42); 
+        map.setCenter(centerLL, 11);
+
+        var overviewCenter = control.ovmap.getCenter();
+        var overviewZoom = control.ovmap.getZoom();
+        t.eq(overviewCenter.lon, -71, "Overviewmap center lon correct");
+        t.eq(overviewCenter.lat, 42, "Overviewmap center lat correct");
+        t.eq(overviewZoom, 8, "Overviewmap zoomcorrect");
+        
+        control.mapDivClick({'xy':new OpenLayers.Pixel(5,5)});
+        
+        var cent = map.getCenter();
+        t.eq(cent.lon, -71.3515625, "Clicking on the Overview Map has the correct effect on map lon");
+        t.eq(cent.lat, 42.17578125, "Clicking on the Overview Map has the correct effect on map lat");
+
+        control.rectMouseDown({'xy':new OpenLayers.Pixel(5,5), 'which':1});
+        control.rectMouseMove({'xy':new OpenLayers.Pixel(15,15), 'which':1});
+        control.rectMouseUp({'xy':new OpenLayers.Pixel(15,15), 'which':1});
+        
+        var cent = map.getCenter();
+        t.eq(cent.lon, -71.2734375, "Dragging on the Overview Map has the correct effect on map lon");
+        t.eq(cent.lat, 42.09765625, "Dragging on the Overview Map has the correct effect on map lat");
+        
+        map.setCenter(new OpenLayers.LonLat(0,0), 0);
+        var overviewCenter = control.ovmap.getCenter();
+        var overviewZoom = control.ovmap.getZoom();
+        t.eq(overviewCenter.lon, 0, "Overviewmap center lon correct -- second zoom");
+        t.eq(overviewCenter.lat, 0, "Overviewmap center lat correct -- second zoom");
+        t.eq(overviewZoom, 0, "Overviewmap zoomcorrect -- second zoom");
+        
+
+    }
+  // -->
+  </script>
+</head>
+<body>
+    <div id="map" style="width: 1024px; height: 512px;"/>
+</body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Control_OverviewMap.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Control_PanZoom.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Control_PanZoom.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Control_PanZoom.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,72 @@
+<html>
+<head>
+  <script src="../lib/OpenLayers.js"></script>
+  <script type="text/javascript"><!--
+    var map; 
+    function test_01_Control_PanZoom_constructor (t) {
+        t.plan( 1 );
+    
+        control = new OpenLayers.Control.PanZoom();
+        t.ok( control instanceof OpenLayers.Control.PanZoom, "new OpenLayers.Control.PanZoom returns object" );
+    }
+    function test_02_Control_PanZoom_addControl (t) {
+        t.plan( 8 );
+        map = new OpenLayers.Map('map');
+        control = new OpenLayers.Control.PanZoom();
+        t.ok( control instanceof OpenLayers.Control.PanZoom, "new OpenLayers.Control.PanZoom returns object" );
+        t.ok( map instanceof OpenLayers.Map, "new OpenLayers.Map creates map" );
+        map.addControl(control);
+        t.ok( control.map === map, "Control.map is set to the map object" );
+        t.ok( map.controls[3] === control, "map.controls contains control" );
+        t.eq( parseInt(control.div.style.zIndex), map.Z_INDEX_BASE['Control'] + 4, "Control div zIndexed properly" );
+        t.eq( parseInt(map.viewPortDiv.lastChild.style.zIndex), map.Z_INDEX_BASE['Control'] + 4, "Viewport div contains control div" );
+        t.eq( control.div.style.top, "4px", "Control div top located correctly by default");
+
+        var control2 = new OpenLayers.Control.PanZoom();
+        map.addControl(control2, new OpenLayers.Pixel(100,100));
+        t.eq( control2.div.style.top, "100px", "2nd control div is located correctly");
+    }
+    function test_03_Control_PanZoom_control_events (t) {
+        t.plan( 7 );
+        var evt = {which: 1}; // control expects left-click
+        map = new OpenLayers.Map('map');
+        var layer = new OpenLayers.Layer.WMS("Test Layer", 
+            "http://octo.metacarta.com/cgi-bin/mapserv?",
+            {map: "/mapdata/vmap_wms.map", layers: "basic"});
+        map.addLayer(layer);
+
+        control = new OpenLayers.Control.PanZoom();
+        map.addControl(control, new OpenLayers.Pixel(20,20));
+
+        var centerLL = new OpenLayers.LonLat(0,0); 
+        map.setCenter(centerLL, 5);
+
+        control.buttons[0].onmousedown(evt);
+        t.ok( map.getCenter().lat > centerLL.lat, "Pan up works correctly" );
+
+        control.buttons[1].onmousedown(evt);
+        t.ok( map.getCenter().lon < centerLL.lon, "Pan left works correctly" );
+
+        control.buttons[2].onmousedown(evt);
+        t.ok( map.getCenter().lon == centerLL.lon, "Pan right works correctly" );
+
+        control.buttons[3].onmousedown(evt);
+        t.ok( map.getCenter().lat == centerLL.lat, "Pan down works correctly" );
+
+        control.buttons[4].onmousedown(evt);
+        t.eq( map.getZoom(), 6, "zoomin works correctly" );
+
+        control.buttons[6].onmousedown(evt);
+        t.eq( map.getZoom(), 5, "zoomout works correctly" );
+
+        control.buttons[5].onmousedown(evt);
+        t.eq( map.getZoom(), 2, "zoomworld works correctly" );
+
+    }
+  // -->
+  </script>
+</head>
+<body>
+    <div id="map" style="width: 1024px; height: 512px;"/>
+</body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Control_PanZoom.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Control_PanZoomBar.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Control_PanZoomBar.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Control_PanZoomBar.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,39 @@
+<html>
+<head>
+  <script src="../lib/OpenLayers.js"></script>
+  <script type="text/javascript"><!--
+    var map; 
+    function test_01_Control_PanZoomBar_constructor (t) {
+        t.plan( 1 );
+    
+        control = new OpenLayers.Control.PanZoomBar();
+        t.ok( control instanceof OpenLayers.Control.PanZoomBar, "new OpenLayers.Control.PanZoomBar returns object" );
+    }
+    function test_02_Control_PanZoomBar_addControl (t) {
+        t.plan( 8 );
+        map = new OpenLayers.Map('map', {controls:[]});
+        var layer = new OpenLayers.Layer.WMS("Test Layer", 
+            "http://octo.metacarta.com/cgi-bin/mapserv?",
+            {map: "/mapdata/vmap_wms.map", layers: "basic"});
+        map.addLayer(layer);
+        control = new OpenLayers.Control.PanZoomBar();
+        t.ok( control instanceof OpenLayers.Control.PanZoomBar, "new OpenLayers.Control.PanZoomBar returns object" );
+        t.ok( map instanceof OpenLayers.Map, "new OpenLayers.Map creates map" );
+        map.addControl(control);
+        t.ok( control.map === map, "Control.map is set to the map object" );
+        t.ok( map.controls[0] === control, "map.controls contains control" );
+        t.eq( parseInt(control.div.style.zIndex), 1001, "Control div zIndexed properly" );
+        t.eq( parseInt(map.viewPortDiv.lastChild.style.zIndex), 1001, "Viewport div contains control div" );
+        t.eq( control.div.style.top, "4px", "Control div top located correctly by default");
+
+        var control2 = new OpenLayers.Control.PanZoomBar();
+        map.addControl(control2, new OpenLayers.Pixel(100,100));
+        t.eq( control2.div.style.top, "100px", "2nd control div is located correctly");
+    }
+  // -->
+  </script>
+</head>
+<body>
+    <div id="map" style="width: 1024px; height: 512px;"/>
+</body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Control_PanZoomBar.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Control_Permalink.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Control_Permalink.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Control_Permalink.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,59 @@
+<html>
+<head>
+  <script src="../lib/OpenLayers.js"></script>
+  <script type="text/javascript"><!--
+    var map; 
+    function test_01_Control_Permalink_constructor (t) {
+        t.plan( 1 );
+    
+        control = new OpenLayers.Control.Permalink();
+        t.ok( control instanceof OpenLayers.Control.Permalink, "new OpenLayers.Control returns object" );
+    }
+    function test_02_Control_Permalink_updateLinks (t) {
+        t.plan( 2 );
+    
+        control = new OpenLayers.Control.Permalink($('permalink'));
+        t.ok( control instanceof OpenLayers.Control.Permalink, "new OpenLayers.Control returns object" );
+        map = new OpenLayers.Map($('map'));
+        layer = new OpenLayers.Layer.WMS('Test Layer', "http://octo.metacarta.com/cgi-bin/mapserv", {map: '/mapdata/vmap_wms.map', layers: 'basic', format: 'image/jpeg'});
+        map.addLayer(layer);
+        if (!map.getCenter())  map.zoomToMaxExtent();
+        map.addControl(control);
+        map.pan(5, 0);
+        if (/MSIE/.test(navigator.userAgent)) {
+            t.eq($('permalink').href, "?lat=0&lon=1.75781&zoom=2&layers=B", "Panning sets permalink");
+        } else {
+            t.eq($('permalink').href, location+"?lat=0&lon=1.75781&zoom=2&layers=B", "Panning sets permalink");
+        }
+    }
+    function test_03_Control_Permalink_updateLinksBase (t) {
+        t.plan( 2 );
+    
+        control = new OpenLayers.Control.Permalink($('permalink'), "./edit.html" );
+        t.ok( control instanceof OpenLayers.Control.Permalink, "new OpenLayers.Control returns object" );
+        map = new OpenLayers.Map($('map'));
+        layer = new OpenLayers.Layer.WMS('Test Layer', "http://octo.metacarta.com/cgi-bin/mapserv", {map: '/mapdata/vmap_wms.map', layers: 'basic', format: 'image/jpeg'});
+        map.addLayer(layer);
+        if (!map.getCenter())  map.zoomToMaxExtent();
+        map.addControl(control);
+        map.pan(5, 0);
+        $('edit_permalink').href = './edit.html?lat=0&lon=1.75781&zoom=2&layers=B';
+        t.eq($('permalink').href, $('edit_permalink').href, "Panning sets permalink with base");
+  }
+  function test_04_Control_Permalink_noElement (t) {
+        t.plan( 2 );
+        control = new OpenLayers.Control.Permalink( );
+        t.ok( control instanceof OpenLayers.Control.Permalink, "new OpenLayers.Control returns object" );
+        map = new OpenLayers.Map($('map'));
+        map.addControl(control);
+        t.eq(map.controls[3].div.firstChild.nodeName, "A", "Permalink control creates div with 'a' inside." );
+  }
+  // -->
+  </script>
+</head>
+<body>
+    <a id="permalink" href="">Permalink</a> <br />
+    <a id="edit_permalink" href="">Edit</a> <br />
+    <div id="map" style="width: 1024px; height: 512px;"/>
+</body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Control_Permalink.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Events.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Events.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Events.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,211 @@
+<html>
+<head>
+  <script src="../lib/OpenLayers.js"></script>
+  <script type="text/javascript"><!--
+    var isMozilla = (navigator.userAgent.indexOf("compatible") == -1);
+    var map; 
+    var a;
+    
+    function test_01_Events_constructor (t) {
+        var mapDiv = $('map');
+        var obj = {result: 0};
+        var eventTypes = ["doThingA", "doThingB", "doThingC"];
+        var browserTypes = OpenLayers.Events.prototype.BROWSER_EVENTS;
+
+        var totalTests = 5 + eventTypes.length + browserTypes.length;
+
+        t.plan( totalTests );
+
+        events = new OpenLayers.Events(obj, mapDiv, eventTypes);
+
+        t.ok( events instanceof OpenLayers.Events, "new OpenLayers.Control returns object" );
+
+        t.ok(events.object ==obj, " 'object' property correctly set");
+        t.ok(events.element == mapDiv, " 'element' property correctly set");
+        t.ok(events.eventTypes == eventTypes, " 'eventTypes' property correctly set");
+
+
+        for (var i = 0; i < browserTypes.length; i++) {
+            t.ok( events.listeners[browserTypes[i]] instanceof Array, 
+                  "browser event: " + browserTypes[i] + " correctly registered" );
+        }
+        for (var i = 0; i < eventTypes.length; i++) {
+            t.ok( events.listeners[eventTypes[i]] instanceof Array, 
+                  "custom event: " + eventTypes[i] + " correctly registered");
+        }
+
+        // default/nulls
+        events = new OpenLayers.Events(null, null, null);
+        t.ok( events.listeners != null,
+              "init of Events with null object/element/eventTypes still creates listeners array" );
+    }
+
+    function test_02_Events_register_unregister(t) {
+ 
+        t.plan( 18 );
+ 
+        var mapDiv = $('map');
+        var obj = {result: 0};
+        var eventTypes = ["doThingA", "doThingB", "doThingC"];
+        
+        events = new OpenLayers.Events(obj, mapDiv, eventTypes);
+        
+        var func = function () { this.result++ }
+        events.register( "doThingA", obj, func );
+
+        var listenerList = events.listeners["doThingA"];
+        t.eq( listenerList.length, 1, "register correctly adds to event.listeners" );
+        t.ok( listenerList[0].obj == obj, "obj property correctly registered");
+        t.ok( listenerList[0].func == func, "func property correctly registered");
+
+        var func2 = function () { this.result-- }
+        events.register( "doThingA", obj, func2 );
+
+        var listenerList = events.listeners["doThingA"];
+        t.eq( listenerList.length, 2, "register correctly appends new callback to event.listeners[doThingA]" );
+        t.ok( listenerList[1].obj == obj, "obj property correctly registered");
+        t.ok( listenerList[1].func == func2, "func property correctly registered");
+
+        var func3 = function () { this.result = this.result + 3; }
+        events.register( "doThingA", null, func3 );
+
+        var listenerList = events.listeners["doThingA"];
+        t.eq( listenerList.length, 3, "register correctly appends new callback to event.listeners[doThingA] even when obj passed in is null" );
+        t.ok( listenerList[2].obj == obj, "obj is correctly set to Events.object default when null is passed in.");
+        t.ok( listenerList[2].func == func3, "func property correctly registered");
+
+        events.register( "doThingA", obj, null);
+
+        var listenerList = events.listeners["doThingA"];
+        t.eq( listenerList.length, 3, "register correctly does not append null callback to event.listeners[doThingA] even when obj passed in is null" );
+
+        events.register( "chicken", obj, func);
+        t.ok( events.listeners["chicken"] == null, "register() does nothing on attempt to register un-enabled event");
+
+        events.unregister("doThingA", obj, null);
+        var listenerList = events.listeners["doThingA"];
+        t.eq( listenerList.length, 3, "trying to unregister a null callback does nothing -- but doesnt crash :-)" );
+    
+        events.unregister("doThingA", obj, func);
+        var found = false;
+        for (var i = 0; i < listenerList.length; i++) {
+            var listener = listenerList[i];
+            if (listener.obj == obj && listener.func == func) {
+                found = true;
+            }
+        }                
+        t.ok( (listenerList.length == 2) && !found, "unregister correctly removes callback" );
+
+        events.unregister("doThingA", null, func3);
+        var found = false;
+        for (var i = 0; i < listenerList.length; i++) {
+            var listener = listenerList[i];
+            if (listener.obj == obj && listener.func == func) {
+                found = true;
+            }
+        }                
+        t.ok( (listenerList.length == 1) && !found, "unregister correctly removes callback when no obj specified" );
+        
+        var func4 = function () { this.result = "chicken"; }
+        events.unregister("doThingA", obj, func4);
+        t.ok( (listenerList.length == 1), "unregister does not bomb if you try to remove an unregistered callback" );
+
+        var obj2 = { chicken: 151 };
+        events.unregister("doThingA", obj2, func2);
+        t.ok( (listenerList.length == 1), "unregister does not bomb or accidntally remove if you try to remove a valid callback on a valid event type, but with the wrong context object" );
+
+        events.unregister("doThingA", obj, null);
+        t.ok( (listenerList.length == 1), "unregister does not bomb if you try to remove a null callback" );
+
+        events.unregister("chicken", null, func3);
+        t.ok( events.listeners["chicken"] == null, "unregistering an event that is not enabled does not wierdly enable it -- or cause a script error")
+
+
+    }
+
+    function test_03_Events_remove(t) {
+
+        t.plan( 2 );
+ 
+        var mapDiv = $('map');
+        var obj = {result: 0};
+        var eventTypes = ["doThingA", "doThingB", "doThingC"];
+        
+        events = new OpenLayers.Events(obj, mapDiv, eventTypes);
+        
+        var func = function () { this.result++ }
+        var func2 = function () { this.result-- }
+        var func3 = function () { this.result = this.result + 3; }
+
+        events.register( "doThingA", obj, func );
+        events.register( "doThingA", obj, func2 );
+        events.register( "doThingA", null, func3 );
+
+        events.remove("doThingA");
+
+        t.eq( events.listeners["doThingA"].length, 0, "remove() correctly clears the event listeners" );
+
+        events.remove("chicken");
+        t.ok( events.listeners["chicken"] == null, "remove on non-enabled event does not break or accidentally enable the event");
+
+    }
+
+    function test_04_Events_triggerEvent(t) {
+    
+        t.plan( 6 );
+ 
+        var mapDiv = $('map');
+        var obj = {result: 0};
+        var eventTypes = ["doThingA", "doThingB", "doThingC"];
+        
+        events = new OpenLayers.Events(obj, mapDiv, eventTypes);
+        
+        
+        var func = function () { this.result++ }
+        events.register( "doThingA", obj, func );
+
+        events.triggerEvent("doThingA", {});
+        t.eq( obj.result, 1, "result is 1 after we call triggerEvent" );
+        events.triggerEvent("doThingA");
+        t.eq( obj.result, 2, "result is 2 after we call triggerEvent with no event" );
+
+        var funcB = function() { this.FUNK = "B"; return false; }
+        events.register( "doThingA", obj, funcB);
+
+        events.triggerEvent("doThingA");
+        t.ok ((obj.result == 3) && (obj.FUNK == "B"), "executing multiple callbacks works")
+
+        var funcZ = function() { this.FUNK = "Z"; }
+        events.register( "doThingA", obj, funcZ);
+
+        events.triggerEvent("doThingA");
+        t.ok ((obj.result == 4) && (obj.FUNK == "B"), "executing multiple callbacks works, and when one returns false, it stops chain correctly")
+
+
+        var func2 = function() { this.result = this.result + 10; }
+        events.register( "doThingB", null, func2);
+        
+        events.triggerEvent("doThingB");
+        t.eq( obj.result, 14, "passing a null object default gets set correctly");
+        
+        //no specific t.ok for this one, but if it breaks, you will know it.
+        events.triggerEvent("chicken");
+
+        events = new OpenLayers.Events(null, mapDiv, eventTypes);
+        
+        // a is global variable (context-irrelevant)
+        a = 0;
+        var func = function () { a = 5; }
+        events.register( "doThingC", null, func );
+        events.triggerEvent("doThingC");
+
+        t.eq(a, 5, "if Events has no object set and an event is registered also with no object, triggerEvent() calls it without trying to set the context to null");        
+    }
+   
+  // -->
+  </script>
+</head>
+<body>
+    <div id="map" style="width: 1024px; height: 512px;"/>
+</body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Events.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Feature.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Feature.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Feature.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,112 @@
+<html>
+<head>
+  <script src="../lib/OpenLayers.js"></script>
+  <script type="text/javascript"><!--
+    var isMozilla = (navigator.userAgent.indexOf("compatible") == -1);
+    var map; 
+    var feature, layer; 
+    
+    function test_01_Feature_constructor (t) {
+        t.plan( 6 );
+
+        var layer = new Object();
+        var lonlat = new OpenLayers.LonLat(2,1);
+        var iconURL = 'http://boston.openguides.org/features/ORANGE.png';
+        var iconSize = new OpenLayers.Size(12, 17);
+        var data =  { iconURL: iconURL,
+                      iconSize: iconSize 
+                    };
+        
+        feature = new OpenLayers.Feature(layer, lonlat, data);
+
+        t.ok( feature instanceof OpenLayers.Feature, "new OpenLayers.Feature returns Feature object" );
+        t.eq( feature.layer, layer, "feature.layer set correctly" );
+        t.ok( feature.id.startsWith("OpenLayers.Feature_"), "feature.id set correctly" );
+        t.ok( feature.lonlat.equals(lonlat), "feature.lonlat set correctly" );
+        t.eq( feature.data.iconURL, iconURL, "feature.data.iconURL set correctly" );
+        t.ok( feature.data.iconSize.equals(iconSize), "feature.data.iconSize set correctly" );
+    }
+    
+    function test_02_Feature_createMarker (t) {
+        t.plan(1);
+        t.ok(true);
+/*
+
+        t.plan( 11 );
+        feature = new OpenLayers.Feature("myfeature", new OpenLayers.LonLat(2,1), 
+               {
+                iconURL:'http://boston.openguides.org/features/ORANGE.png',
+                iconW: 12,
+                iconH: 17
+               });
+        layer = new OpenLayers.Layer.Markers('Marker Layer');
+        t.ok( feature instanceof OpenLayers.Feature, "new OpenLayers.Feature returns Feature object" );
+        t.ok( layer instanceof OpenLayers.Layer.Markers, "Layer is a marker layer" );
+        feature.createMarker(layer);
+        
+        t.ok( feature.marker instanceof OpenLayers.Marker, 
+              "createMarker sets a marker property to a marker" );
+        t.ok( layer.markers[0] === feature.marker, 
+              "First marker in layer is the feature marker" );
+        
+        t.ok( feature.marker.lonlat instanceof OpenLayers.LonLat, 
+              "createMarker sets a marker lontlat property to a lonlat" );
+        t.ok( layer.markers[0].lonlat === feature.lonlat, 
+              "First marker in the layer matches feature lonlat" );
+        
+        t.ok( feature.marker.icon instanceof OpenLayers.Icon, 
+              "createMarker sets a marker icon property to an icon" );
+        
+        t.eq( feature.marker.icon.url, 
+              "http://boston.openguides.org/features/ORANGE.png", 
+              "createMarker sets marker url correctly" );
+       
+        var map = new OpenLayers.Map('map');
+        map.addLayer(layer);
+        map.setCenter(new OpenLayers.LonLat(0,0),0);
+        t.ok( map.layers[0] == layer,
+              "Marker layer added to map okay." );
+        if (!isMozilla)
+            t.ok( true, "skipping element test outside of Mozilla");
+        else
+            t.ok( map.layers[0].div.firstChild instanceof HTMLImageElement,
+                  "layer div firstChild is an image" );
+        t.eq( map.layers[0].div.firstChild.src,
+              "http://boston.openguides.org/features/ORANGE.png", 
+              "Layer div img contains correct url" );
+*/
+    }
+    
+    function test_03_Feature_onScreen(t) {
+        t.plan( 2 );
+
+        var map = new OpenLayers.Map("map");
+
+        var url = "http://octo.metacarta.com/cgi-bin/mapserv";
+        var wms = new OpenLayers.Layer.WMS(name, url);
+
+        map.addLayer(wms);
+
+        var layer = new OpenLayers.Layer("foo");
+        map.addLayer(layer);
+        
+        map.zoomToExtent(new OpenLayers.Bounds(-50,-50,50,50));
+
+        //onscreen feature
+        var feature1 = new OpenLayers.Feature(layer, 
+                                              new OpenLayers.LonLat(0,0));
+        t.ok( feature1.onScreen(), "feature knows it's onscreen" );
+
+        //onscreen feature
+        var feature2 = new OpenLayers.Feature(layer, 
+                                              new OpenLayers.LonLat(100,100));
+        t.ok( !feature2.onScreen(), "feature knows it's offscreen" );
+    }
+
+  // -->
+  </script>
+</head>
+<body>
+  <div id="map" style="width: 500px; height: 300px;"></div>
+</body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Feature.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Icon.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Icon.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Icon.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,44 @@
+<html>
+<head>
+  <script src="../lib/OpenLayers.js"></script>
+  <script type="text/javascript"><!--
+    var icon; 
+    
+    function test_01_Icon_constructor (t) {
+        t.plan( 4 );
+        var size = new OpenLayers.Size(5,6);
+        icon = new OpenLayers.Icon("", size);
+        t.ok( icon instanceof OpenLayers.Icon, "new OpenLayers.Icon returns Icon object" );
+        t.ok( icon.size instanceof OpenLayers.Size, "icon.size returns Size object" );
+        t.ok( icon.size.equals(size), "icon.size returns correct value" );
+        t.eq( icon.url, "", "icon.url returns str object" );
+    }
+    function test_02_Icon_clone (t) {
+        t.plan( 4 );
+        icon = new OpenLayers.Icon("a",new OpenLayers.Size(5,6));
+        t.ok( icon instanceof OpenLayers.Icon, "new OpenLayers.Icon returns Icon object" );
+        var cloned = icon.clone();
+        t.ok( cloned instanceof OpenLayers.Icon, "clone is an OpenLayers.Icon" );
+        cloned.url = "b"
+        t.eq( icon.url, "a", "icon.url doesn't change with clone's url" );
+        t.eq( cloned.url, "b", "cloned.url does change when edited" );
+    }
+
+    function test_02_Marker_setOpacity(t) {
+        t.plan( 2 );
+        
+        icon = new OpenLayers.Icon("a",new OpenLayers.Size(5,6));
+
+        t.ok(!icon.imageDiv.style.opacity, "default icon has no opacity");
+        
+        icon.setOpacity(0.5);
+
+        t.eq(icon.imageDiv.style.opacity + "", "0.5", "icon.setOpacity() works");
+    }
+
+  // -->
+  </script>
+</head>
+<body>
+</body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Icon.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,185 @@
+<html>
+<head>
+  <script src="../lib/OpenLayers.js"></script>
+  <script type="text/javascript"><!--
+    var layer; 
+
+    function test_01_Layer_constructor (t) {
+        t.plan( 13 );
+        
+        var options = { chicken: 151, foo: "bar", projection: "none" };
+        var layer = new OpenLayers.Layer('Test Layer', options);
+        
+        t.ok( layer instanceof OpenLayers.Layer, "new OpenLayers.Layer returns object" );
+        t.eq( layer.CLASS_NAME, "OpenLayers.Layer", "CLASS_NAME variable set correctly");
+
+        t.eq( layer.name, "Test Layer", "layer.name is correct" );
+        t.ok( layer.id != null, "Layer is given an id");
+        t.ok( layer.projection, "none", "default layer projection correctly set");
+        t.ok( ((layer.chicken == 151) && (layer.foo == "bar")), "layer.options correctly set to Layer Object" );
+        t.ok( ((layer.options["chicken"] == 151) && (layer.options["foo"] == "bar")), "layer.options correctly backed up" );
+
+        options.chicken = 552;
+        
+        t.eq( layer.options["chicken"], 151 , "layer.options correctly made fresh copy" );
+        
+        t.eq( layer.isBaseLayer, false, "Default layer is not base layer" );
+
+        layer = new OpenLayers.Layer('Test Layer');
+        t.ok( layer instanceof OpenLayers.Layer, "new OpenLayers.Layer returns object" );
+        t.eq( layer.name, "Test Layer", "layer.name is correct" );
+        t.ok( layer.projection == null, "default layer projection correctly set");
+        t.ok( layer.options instanceof Object, "layer.options correctly initialized as a non-null Object" );
+    }
+
+    function test_02_Layer_clone (t) {
+        t.plan( 8 );
+        
+        var mapone = new OpenLayers.Map('map'); 
+        var options = { chicken: 151, foo: "bar", maxResolution: "auto" };
+        var layer = new OpenLayers.Layer('Test Layer', options);
+        mapone.addLayer(layer);
+
+        // randomly assigned property
+        layer.chocolate = 5;
+
+        var clone = layer.clone();
+
+        t.ok( clone.map == null, "cloned layer has map property set to null")
+
+        var maptwo = new OpenLayers.Map('map2'); 
+        maptwo.addLayer(clone);
+
+        t.ok( clone instanceof OpenLayers.Layer, "new OpenLayers.Layer returns object" );
+        t.eq( clone.name, "Test Layer", "default clone.name is correct" );
+        t.ok( ((clone.options["chicken"] == 151) && (clone.options["foo"] == "bar")), "clone.options correctly set" );
+        t.eq(clone.chocolate, 5, "correctly copied randomly assigned property");
+
+        layer.addOptions({chicken:152});
+        t.eq(clone.options["chicken"], 151, "made a clean copy of options");        
+
+        t.ok( (layer.maxResolution != clone.maxResolution), "maxresolution of clone reset to new map div");
+        t.ok( (layer.minResolution != clone.minResolution), "minresolution of clone reset to new map div");
+        
+    }
+
+    function test_03_Layer_setName (t) {
+        
+        t.plan( 1 );
+
+        layer = new OpenLayers.Layer('Test Layer');
+        layer.setName("chicken");
+        
+        t.eq(layer.name, "chicken", "setName() works")
+        
+    }
+
+    function test_04_Layer_addOptions (t) {
+        
+        t.plan( 4 );
+
+        var options = { chicken: 151, foo: "bar" };
+        var layer = new OpenLayers.Layer('Test Layer', options);
+
+        layer.addOptions({bark:55, chicken: 171});
+
+        t.eq(layer.bark, 55, "addOptions() assigns new option correctly to Layer");
+        t.eq(layer.options.bark, 55, "addOptions() adds new option correctly to backup");
+
+        t.eq(layer.chicken, 171, "addOptions() overwrites option correctly to Layer");
+        t.eq(layer.options.chicken, 171, "addOptions() overwrites option correctly to backup");
+
+    }
+    
+    function test_04_Layer_StandardOptionsAccessors (t) {
+
+        t.plan( 4 );
+
+        var projection = "chicken";
+        var maxExtent = new OpenLayers.Bounds(50,50,100,100);
+        var maxResolution = 1.5726;
+        var numZoomLevels = 11;
+
+        var options = { projection: projection, 
+                        maxExtent: maxExtent,
+                        maxResolution: maxResolution,
+                        numZoomLevels: numZoomLevels
+                        };
+
+        var layer = new OpenLayers.Layer('Test Layer', options);
+
+        t.eq(layer.projection, projection, "projection set correctly");
+        t.ok(layer.maxExtent.equals(maxExtent), "maxExtent set correctly");
+        t.eq(layer.maxResolution, maxResolution, "maxResolution set correctly");
+        t.eq(layer.numZoomLevels, numZoomLevels, "numZoomLevels set correctly");
+    }
+
+    function test_05_Layer_visibility(t) {
+
+        t.plan(3)
+
+        var layer = new OpenLayers.Layer('Test Layer');
+    
+        t.eq(layer.getVisibility(), true, "default for layer creation is visible");
+        
+        layer.setVisibility(false);
+        t.eq(layer.getVisibility(), false, "setVisibility false works");
+
+        layer.setVisibility(true);
+        t.eq(layer.getVisibility(), true, "setVisibility true works");
+    
+    }
+
+
+    function test_06_Layer_getZoomForResolution(t) {
+
+        t.plan(4);
+
+        var layer = new OpenLayers.Layer('Test Layer');
+            
+        //make some dummy resolutions
+        layer.resolutions = [128, 64, 32, 16, 8, 4, 2];
+        
+        t.eq(layer.getZoomForResolution(200), 0, "zoom all the way out");
+        t.eq(layer.getZoomForResolution(25), 2, "zoom in middle");
+        t.eq(layer.getZoomForResolution(3), 5, "zoom allmost all the way in");
+        t.eq(layer.getZoomForResolution(1), 6, "zoom  all the way in");
+
+    }
+    
+    
+/******
+ * 
+ * 
+ * HERE IS WHERE SOME TESTS SHOULD BE PUT TO CHECK ON THE LONLAT-PX TRANSLATION
+ * FUNCTIONS AND RESOLUTION AND GETEXTENT GETZOOMLEVEL, ETC
+ * 
+ * 
+ */
+
+
+    function test_99_Layer_destroy (t) {
+        t.plan( 4 );    
+
+        var map = new OpenLayers.Map('map');
+        
+        layer = new OpenLayers.Layer('Test Layer');
+
+        map.addLayer(layer);
+
+        layer.destroy();
+
+        t.eq( layer.name, null, "layer.name is null after destroy" );
+        t.eq( layer.div, null, "layer.div is null after destroy" );
+        t.eq( layer.map, null, "layer.map is null after destroy" );
+        t.eq( layer.options, null, "layer.options is null after destroy" );
+
+    }
+  // -->
+  </script>
+</head>
+<body>
+  <div id="map" style="width:500px;height:500px"></div>
+  <div id="map2" style="width:100px;height:100px"></div>
+</body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_EventPane.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_EventPane.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_EventPane.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,113 @@
+<html>
+<head>
+  <script src="../lib/OpenLayers.js"></script>
+  <script type="text/javascript"><!--
+    var isMozilla = (navigator.userAgent.indexOf("compatible") == -1);
+    var layer; 
+
+    function test_01_Layer_EventPane_constructor (t) {
+        t.plan( 5 );
+        
+        var layer = new OpenLayers.Layer.EventPane('Test Layer');
+        
+        t.ok( layer instanceof OpenLayers.Layer.EventPane, "new OpenLayers.Layer.EventPane returns object" );
+        t.eq( layer.CLASS_NAME, "OpenLayers.Layer.EventPane", "CLASS_NAME variable set correctly");
+        t.eq( layer.name, "Test Layer", "layer.name is correct" );
+        t.eq( layer.isBaseLayer, true, "EventPane layer is always base layer" );
+        if (!isMozilla) {
+            t.ok( true, "skipping element test outside of Mozilla");
+        } else {
+            t.ok( layer.pane instanceof HTMLDivElement, "layer.pane is an HTMLDivElement" );
+        }
+    }
+
+    function test_02_Layer_EventPane_clone (t) {
+        t.plan( 1 );
+        t.ok( true, "need to actually write some tests here" );
+        return;
+        
+        /// FIX ME FIX ME: fix this later
+
+        var map = new OpenLayers.Map('map'); 
+        var options = { chicken: 151, foo: "bar" };
+        var layer = new OpenLayers.Layer('Test Layer', options);
+        map.addLayer(layer);
+
+        // randomly assigned property
+        layer.chocolate = 5;
+
+        var clone = layer.clone();
+
+        t.ok( clone instanceof OpenLayers.Layer, "new OpenLayers.Layer returns object" );
+        t.eq( clone.name, "Test Layer", "default clone.name is correct" );
+        t.ok( ((clone.options["chicken"] == 151) && (clone.options["foo"] == "bar")), "clone.options correctly set" );
+        t.eq(clone.chocolate, 5, "correctly copied randomly assigned property");
+
+        layer.addOptions({chicken:152});
+        t.eq(clone.options["chicken"], 151, "made a clean copy of options");        
+
+        
+        t.ok( clone.map == null, "cloned layer has map property set to null")
+        
+    }
+
+    function test_10_Layer_EventPane_setMap (t) {
+
+// MOUSEMOVE test does not seem to work... 
+//         t.plan( 2 );    
+
+        if (document.createEventObject) {
+          t.plan(4);
+        } else {
+          t.plan(3);
+        }
+        var map = new OpenLayers.Map('map');
+        
+        layer = new OpenLayers.Layer.EventPane('Test Layer');
+
+        //give dummy function so test wont bomb on layer.setMap()
+        layer.loadMapObject = function() { };
+        layer.getWarningHTML = function() { this.warning = true; return ""; };
+        map.addLayer(layer);
+        t.eq( parseInt(layer.pane.style.zIndex) - parseInt(layer.div.style.zIndex),
+            1, "layer pane is 1 z-level above its div" );
+
+        t.ok( layer.warning, "warning correctly registered on no mapObject load" );
+
+        layer2 = new OpenLayers.Layer.EventPane('Test Layer');
+
+        //give dummy function so test wont bomb on layer.setMap()
+        layer2.loadMapObject = function() { this.mapObject = new Object(); };
+        layer2.getWarningHTML = function() { this.warning = true; return ""; }
+
+        map.addLayer(layer2);
+        t.ok( !layer2.warning, "warning not registered on mapObject load" );
+
+        map.events.register("mousemove", map, function () {
+            t.ok(true, "got mouse move");
+        });
+        
+        if( document.createEvent ) { // Mozilla
+          var evObj = document.createEvent('MouseEvents');
+          evObj.initEvent( 'mousemove', true, false );
+          layer.pane.dispatchEvent(evObj);
+        } else if( document.createEventObject ) { // IE
+          layer.pane.fireEvent('onmousemove');
+        }
+    }
+
+    function test_20_Layer_EventPane_setVisibility (t) {
+        t.plan( 2 );    
+        layer = new OpenLayers.Layer.EventPane('Test Layer');
+        layer.setVisibility(false);
+        t.eq(layer.visibility, false, "layer pane is now invisible");
+        layer.setVisibility(true);
+        t.eq(layer.visibility, true, "layer pane is now visible");
+    }
+  // -->
+  </script>
+</head>
+<body>
+  <div id="map" style="height:500px;width:500px"></div>
+</body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_EventPane.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_FixedZoomLevels.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_FixedZoomLevels.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_FixedZoomLevels.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,97 @@
+<html>
+<head>
+  <script src="../lib/OpenLayers.js"></script>
+  <script type="text/javascript"><!--
+    var layer; 
+
+    function test_01_Layer_FixedZoomLevels (t) {
+        t.plan( 36 );
+        
+        var layer = { 'MIN_ZOOM_LEVEL': 5,
+                      'MAX_ZOOM_LEVEL': 10 };        
+
+
+    //defaults 
+
+        layer = p_createLayer(layer);
+        p_minMaxNum(t, layer, layer.MIN_ZOOM_LEVEL, layer.MAX_ZOOM_LEVEL, "nothing specified");
+
+    
+    //layer.options
+
+        // min,num
+        layer = p_createLayer(layer, {}, { minZoomLevel: 3, numZoomLevels: 12});
+        p_minMaxNum(t, layer, layer.MIN_ZOOM_LEVEL, layer.MAX_ZOOM_LEVEL, "min too low num too high(layer.options)");
+
+        layer = p_createLayer(layer, {}, { minZoomLevel: 6, numZoomLevels: 3 });
+        p_minMaxNum(t, layer, 6, 8, "valid min,num(layer.options)");
+
+
+        // max
+        layer = p_createLayer(layer, {}, { maxZoomLevel: 9 });
+        p_minMaxNum(t, layer, layer.MIN_ZOOM_LEVEL, 9, "valid max(layer.options)");
+
+        layer = p_createLayer(layer, {}, { maxZoomLevel: 12 });
+        p_minMaxNum(t, layer, layer.MIN_ZOOM_LEVEL, layer.MAX_ZOOM_LEVEL, "invalid max(layer.options)");
+
+
+
+    //map
+
+        // min,num
+        layer = p_createLayer(layer, { minZoomLevel: 3, numZoomLevels: 12});
+        p_minMaxNum(t, layer, layer.MIN_ZOOM_LEVEL, layer.MAX_ZOOM_LEVEL, "min too low num too high(map)");
+
+        layer = p_createLayer(layer, { minZoomLevel: 6, numZoomLevels: 3 });
+        p_minMaxNum(t, layer, 6, 8, "valid min,num(map)");
+
+
+        // max
+        layer = p_createLayer(layer, { maxZoomLevel: 9 });
+        p_minMaxNum(t, layer, layer.MIN_ZOOM_LEVEL, 9, "valid max(map)");
+
+        layer = p_createLayer(layer, { maxZoomLevel: 12 });
+        p_minMaxNum(t, layer, layer.MIN_ZOOM_LEVEL, layer.MAX_ZOOM_LEVEL, "invalid max(map)");
+
+    //map vs. options
+
+        layer = p_createLayer(layer, {minZoomLevel: 6, numZoomLevels: 2}, { minZoomLevel: 7, numZoomLevels: 3});
+        p_minMaxNum(t, layer, 7, 9, "min,num(layer.options) wins over (map)");
+
+        layer = p_createLayer(layer, {minZoomLevel: 6, maxZoomLevel: 8}, { minZoomLevel: 7, maxZoomLevel: 9});
+        p_minMaxNum(t, layer, 7, 9, "min,max(layer.options) wins over (map)");
+
+
+    // numZoomLevels vs. maxZoomLevel
+    
+        layer = p_createLayer(layer, {maxZoomLevel: 8, numZoomLevels: 6});
+        p_minMaxNum(t, layer, layer.MIN_ZOOM_LEVEL, 10, "min,max(layer.options) wins over (map)");
+
+
+
+    }
+
+    function p_createLayer(layer, mapOptions, layerOptions) {
+
+        layer.map = mapOptions || {};
+        layer.options = layerOptions || {};
+        OpenLayers.Layer.FixedZoomLevels.prototype.initResolutions.apply(layer);
+
+        return layer;
+    }
+
+    function p_minMaxNum(t, layer, min, max, msg) {
+
+        t.eq( layer.minZoomLevel, min, "min zoom level inherited from layer constant: " + msg);
+        t.eq( layer.maxZoomLevel, max, "max zoom level inherited from layer constant: " + msg);
+        t.eq( layer.numZoomLevels, max - min + 1, "num zoom levels correctly calcuated: " + msg);
+        
+    }
+
+  // -->
+  </script>
+</head>
+<body>
+  <div id="map"></div>
+</body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_FixedZoomLevels.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_GeoRSS.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_GeoRSS.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_GeoRSS.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,73 @@
+<html>
+<head>
+  <script src="../lib/OpenLayers.js"></script>
+  <script type="text/javascript"><!--
+    var isMozilla = (navigator.userAgent.indexOf("compatible") == -1);
+    var layer; 
+
+    function test_01_Layer_GeoRSS_constructor (t) {
+        t.plan( 5 );
+        layer = new OpenLayers.Layer.GeoRSS('Test Layer', "./georss.txt" );
+        t.ok( layer instanceof OpenLayers.Layer.GeoRSS, "new OpenLayers.Layer.GeoRSS returns object" );
+        t.eq( layer.location, "./georss.txt", "layer.location is correct" );
+        var markers;
+        t.delay_call( 1, function() {  
+            t.eq( layer.markers.length, 40, "marker length is correct" );
+            var ll = new OpenLayers.LonLat(-71.142197, 42.405696);
+            t.ok( layer.markers[0].lonlat.equals(ll), "lonlat on first marker is correct" );
+            t.eq( layer.name, "Crschmidt's Places At Platial", "Layer name is correct." );
+        } );
+    }
+    function test_02_Layer_GeoRSS_draw (t) { 
+//        t.plan(5);
+        t.plan( 2 );
+        layer = new OpenLayers.Layer.GeoRSS('Test Layer', './georss.txt');
+        t.ok( layer instanceof OpenLayers.Layer.GeoRSS, "new OpenLayers.Layer.GeoRSS returns object" );
+        var map = new OpenLayers.Map('map');
+        var baseLayer = new OpenLayers.Layer.WMS("Test Layer", 
+            "http://octo.metacarta.com/cgi-bin/mapserv?",
+            {map: "/mapdata/vmap_wms.map", layers: "basic"});
+        map.addLayer(baseLayer);
+        map.addLayer(layer);
+        t.delay_call( 1, function() { 
+          map.setCenter(new OpenLayers.LonLat(0,0),0);
+          t.eq( map.layers[1].name, layer.name, "Layer name is correct" );
+
+        });;
+    }
+    function test_03_Layer_GeoRSS_events (t) {
+        t.plan( 4 );    
+        layer = new OpenLayers.Layer.GeoRSS('Test Layer', './georss.txt');
+        var map = new OpenLayers.Map('map');
+        var baseLayer = new OpenLayers.Layer.WMS("Test Layer", 
+            "http://octo.metacarta.com/cgi-bin/mapserv?",
+            {map: "/mapdata/vmap_wms.map", layers: "basic"});
+        map.addLayer(baseLayer);
+        map.addLayer(layer);
+        map.setCenter(new OpenLayers.LonLat(0,0),0);
+        var event = {};
+        t.delay_call( 1, function() {  
+          t.ok(layer.markers[0].events, "First marker has an events object");
+          t.eq(layer.markers[0].events.listeners['click'].length, 1, "Marker events has one object");
+          layer.markers[0].events.triggerEvent('click', event);
+          t.eq(map.popups.length, 1, "Popup opened correctly");
+          layer.markers[1].events.triggerEvent('click', event);
+          t.eq(map.popups.length, 1, "1st popup gone, 2nd Popup opened correctly");
+        });
+    }
+
+    function test_99_Layer_GeoRSS_destroy (t) {
+        t.plan( 1 );    
+        layer = new OpenLayers.Layer.GeoRSS('Test Layer', './georss.txt');
+        var map = new OpenLayers.Map('map');
+        map.addLayer(layer);
+        layer.destroy();
+        t.eq( layer.map, null, "layer.map is null after destroy" );
+    }
+  // -->
+  </script>
+</head>
+<body>
+  <div id="map" style="width:500px; height:500px"></div>
+</body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_GeoRSS.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_Google.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_Google.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_Google.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,156 @@
+<html>
+<head>
+  <!-- this gmaps key generated for http://openlayers.org/dev/ -->
+  <script src='http://maps.google.com/maps?file=api&amp;v=2&amp;key=ABQIAAAA9XNhd8q0UdwNC7YSO4YZghSPUCi5aRYVveCcVYxzezM4iaj_gxQ9t-UajFL70jfcpquH5l1IJ-Zyyw'></script>
+  <script src="../lib/OpenLayers.js"></script>
+  <script type="text/javascript"><!--
+    var layer; 
+
+    function test_01_Layer_Google_constructor (t) {
+        t.plan( 4 );
+        
+        var map = new OpenLayers.Map('map');
+        var layer = new OpenLayers.Layer.Google('Goog Layer');
+        map.addLayer(layer);
+        
+        
+        t.ok( layer instanceof OpenLayers.Layer.Google, "new OpenLayers.Layer.Google returns object" );
+        t.eq( layer.CLASS_NAME, "OpenLayers.Layer.Google", "CLASS_NAME variable set correctly");
+
+        t.eq( layer.name, "Goog Layer", "layer.name is correct" );
+
+        t.ok ( layer.gmap != null, "GMap2 Object correctly loaded");
+    }
+
+    function test_02_Layer_Google_isBaseLayer (t) {
+        t.plan(1);
+        
+        var layer = new OpenLayers.Layer.Google('Goog Layer');
+
+        t.ok(layer.isBaseLayer, "a default load of google layer responds as a base layer");
+    }    
+
+    function test_03_Layer_Google_Translation_zoom (t) {
+        
+        t.plan( 4 );
+        
+        var map = new OpenLayers.Map('map');
+        var layer = new OpenLayers.Layer.Google('Goog Layer');
+        map.addLayer(layer);
+
+     // these two lines specify an appropriate translation. 
+     //  the code afterwards works by itself to test that translation
+     //  works correctly both ways.
+        var gZoom = 5;
+        var correspondingOLZoom = 5;
+
+
+        olZoom = layer.getOLZoomFromGZoom(gZoom);
+        t.eq(olZoom, correspondingOLZoom, "Translation from GZoom to OL Zoom works");
+        t.eq(layer.getGZoomFromOLZoom(olZoom), gZoom, "Translation from OL Zoom to GZoom works");
+
+        t.ok( layer.getGZoomFromOLZoom(null) == null, "getGZoomFromOLZoom(null) returns null");
+        t.ok( layer.getOLZoomFromGZoom(null) == null, "getOLZoomFromGZoom(null) returns null");
+    }
+
+    function test_04_Layer_Google_Translation_lonlat (t) {
+        
+        t.plan( 4 );
+        
+        var map = new OpenLayers.Map('map');
+        var layer = new OpenLayers.Layer.Google('Goog Layer');
+        map.addLayer(layer);
+
+     // these two lines specify an appropriate translation. 
+     //  the code afterwards works by itself to test that translation
+     //  works correctly both ways.
+        var gLatLng = new GLatLng(50,100);
+        var correspondingOLLonLat = new OpenLayers.LonLat(100,50);
+
+
+        olLonLat = layer.getOLLonLatFromGLatLng(gLatLng);
+        t.ok(olLonLat.equals(correspondingOLLonLat), "Translation from GLatLng to OpenLayers.LonLat works");
+
+        var transGLatLng = layer.getGLatLngFromOLLonLat(olLonLat);
+        t.ok( transGLatLng.equals(gLatLng), "Translation from OpenLayers.LonLat to GLatLng works");
+
+        t.ok( layer.getGLatLngFromOLLonLat(null) == null, "getGLatLngFromOLLonLat(null) returns null");
+        t.ok( layer.getOLLonLatFromGLatLng(null) == null, "getOLLonLatFromGLatLng(null) returns null");
+    }
+
+    function test_05_Layer_Google_Translation_pixel (t) {
+        
+        t.plan( 4 );
+        
+        var map = new OpenLayers.Map('map');
+        var layer = new OpenLayers.Layer.Google('Goog Layer');
+        map.addLayer(layer);
+
+     // these two lines specify an appropriate translation. 
+     //  the code afterwards works by itself to test that translation
+     //  works correctly both ways.
+        var gPoint = new GPoint(50,100);
+        var correspondingOLPixel = new OpenLayers.Pixel(50, 100);
+
+
+        olPixel = layer.getOLPixelFromGPoint(gPoint);
+        t.ok( olPixel.equals(correspondingOLPixel), "Translation from GPoint to OpenLayers.Pixel works");
+
+        var transGPoint = layer.getGPointFromOLPixel(olPixel);
+        t.ok( transGPoint.equals(gPoint), "Translation from OpenLayers.Pixel to GPoint works");
+
+        t.ok( layer.getGPointFromOLPixel(null) == null, "getGPointFromOLPixel(null) returns null");
+        t.ok( layer.getOLPixelFromGPoint(null) == null, "getOLPixelFromGPoint(null) returns null");
+    }
+
+    function test_06_Layer_Google_Translation_bounds (t) {
+        
+        t.plan( 4 );
+        
+        var map = new OpenLayers.Map('map');
+        var layer = new OpenLayers.Layer.Google('Goog Layer');
+        map.addLayer(layer);
+
+     // these two lines specify an appropriate translation. 
+     //  the code afterwards works by itself to test that translation
+     //  works correctly both ways.
+        var sw = new GLatLng(10,20);
+        var ne = new GLatLng(50,40)
+        var gBounds = new GLatLngBounds(sw, ne);
+        var correspondingOLBounds = new OpenLayers.Bounds(20, 10, 40, 50);
+
+
+        olBounds = layer.getOLBoundsFromGLatLngBounds(gBounds);
+
+        t.ok(olBounds.equals(correspondingOLBounds), "Translation from GLatLngBounds to OpenLayers.Bounds works");
+
+        var transGBounds = layer.getGLatLngBoundsFromOLBounds(olBounds);
+        t.ok( transGBounds.equals(gBounds), "Translation from OpenLayers.Bounds to GLatLngBounds works");
+
+        t.ok( layer.getGLatLngBoundsFromOLBounds(null) == null, "getGLatLngBoundsFromOLBounds(null) returns null");
+        t.ok( layer.getOLBoundsFromGLatLngBounds(null) == null, "getOLBoundsFromGLatLngBounds(null) returns null");
+    }
+
+    function test_99_Layer_destroy (t) {
+        t.plan( 5 );    
+
+        var map = new OpenLayers.Map('map');
+        
+        layer = new OpenLayers.Layer.Google('Test Layer');
+        map.addLayer(layer);
+
+        layer.destroy();
+
+        t.eq( layer.name, null, "layer.name is null after destroy" );
+        t.eq( layer.div, null, "layer.div is null after destroy" );
+        t.eq( layer.map, null, "layer.map is null after destroy" );
+        t.eq( layer.options, null, "layer.options is null after destroy" );
+        t.eq( layer.gmap, null, "layer.gmap is null after destroy" );
+    }
+  // -->
+  </script>
+</head>
+<body>
+  <div id="map"></div>
+</body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_Google.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_Grid.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_Grid.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_Grid.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,206 @@
+<html>
+<head>
+  <script src="../lib/OpenLayers.js"></script>
+  <script type="text/javascript"><!--
+    var isMozilla = (navigator.userAgent.indexOf("compatible") == -1);
+    var layer; 
+
+    var name = 'Test Layer';
+    var url = "http://octo.metacarta.com/cgi-bin/mapserv";
+    var params = { map: '/mapdata/vmap_wms.map', 
+                   layers: 'basic', 
+                   format: 'image/png'};
+
+    /**
+     *  NOTE TO READER:
+     * 
+     *    Some of the tests on the Grid class actually use the WMS class. 
+     *    This is because WMS is a subclass of Grid and it implements the 
+     *    core functions which are necessary to test the tile-generation 
+     *    mechanism. 
+     * 
+     */
+
+
+    function test_01_Layer_Grid_constructor (t) {
+        t.plan( 1 );
+                       
+        layer = new OpenLayers.Layer.Grid(name, url, params, null);
+        t.ok( layer instanceof OpenLayers.Layer.Grid, "returns OpenLayers.Layer.Grid object" );
+    }
+
+
+    function test_02_Layer_Grid_inittiles (t) {
+        t.plan( 2 );
+        var map = new OpenLayers.Map($('map'));
+        layer = new OpenLayers.Layer.WMS(name, url, params);
+        map.addLayer(layer);
+        map.setCenter(new OpenLayers.LonLat(0,0),5);
+        t.eq( layer.grid.length, 7, "Grid rows is correct." );
+        t.eq( layer.grid[0].length, 6, "Grid cols is correct." );
+        
+    }
+
+    function test_03_Layer_Grid_clearTiles (t) {
+        t.plan(1);
+        var map = new OpenLayers.Map('map');
+        layer = new OpenLayers.Layer.WMS(name, url, params);
+        map.addLayer(layer);
+
+        map.setCenter(new OpenLayers.LonLat(0,0));
+
+
+        //grab a reference to one of the tiles
+        var tile = layer.grid[0][0];        
+
+        layer.clearGrid();
+
+        t.ok( layer.grid != null, "layer.grid does not get nullified" );
+    }
+
+
+    function test_04_Layer_Grid_getGridBounds(t) {
+        t.plan( 1 );
+
+        layer = new OpenLayers.Layer.WMS(name, url, params);
+
+        var bl = { bounds: new OpenLayers.Bounds(1,2,0,0)};
+        var tr = { bounds: new OpenLayers.Bounds(0,0,3,4)};
+        layer.grid = [ [6, tr], 
+                       [bl, 7]];
+
+        var bounds = layer.getGridBounds();
+    
+        var testBounds = new OpenLayers.Bounds(1,2,3,4);
+        
+        t.ok( bounds.equals(testBounds), "getGridBounds() returns correct bounds")
+
+        layer.grid = null;
+    }
+
+    function test_05_Layer_Grid_getResolution(t) {
+        t.plan( 1 );
+
+        var map = new OpenLayers.Map('map');
+        layer = new OpenLayers.Layer.WMS(name, url, params);
+        map.addLayer(layer);
+
+        map.zoom = 5;
+
+        t.eq( layer.getResolution(), 0.0439453125, "getResolution() returns correct value");
+    }
+
+    function test_06_Layer_Grid_getZoomForExtent(t) {
+        t.plan( 2 );
+        var bounds, zoom;
+
+        var map = new OpenLayers.Map('map');
+        layer = new OpenLayers.Layer.WMS(name, url, params);
+        map.addLayer(layer);
+
+        bounds = new OpenLayers.Bounds(10,10,12,12);
+        zoom = layer.getZoomForExtent(bounds);
+
+        t.eq( zoom, 8, "getZoomForExtent() returns correct value");
+
+        bounds = new OpenLayers.Bounds(10,10,100,100);
+        zoom = layer.getZoomForExtent(bounds);
+
+        t.eq( zoom, 2, "getZoomForExtent() returns correct value");
+    }   
+
+
+    /** THIS WOULD BE WHERE THE TESTS WOULD GO FOR 
+     *     
+     *    -moveTo
+     *    -insertColumn
+     *    -insertRow
+    
+    function 07_Layer_Grid_moveTo(t) {
+    }
+
+    function 08_Layer_Grid_insertColumn(t) {
+    }
+
+    function 09_Layer_Grid_insertRow(t) {
+    }
+
+     * 
+     */
+
+    function test_10_Layer_Grid_clone(t) {
+        t.plan(5);
+        
+        var options = {tileSize: new OpenLayers.Size(500,50)};
+        var map = new OpenLayers.Map('map', options);
+        layer = new OpenLayers.Layer.Grid(name, url, params);
+        map.addLayer(layer);
+
+        layer.grid = [ [6, 7], 
+                       [8, 9]];
+
+        var clone = layer.clone();
+
+        t.ok( clone.grid != layer.grid, "clone does not copy grid");
+        t.ok( clone.grid.length == 0, "clone creates a new array instead");
+
+        t.ok( clone.tileSize.equals(layer.tileSize), "tileSize correctly cloned");
+
+        layer.tileSize.w += 40;
+
+        t.eq( clone.tileSize.w, 500, "changing layer.tileSize does not change clone.tileSize -- a fresh copy was made, not just copied reference");
+
+        t.eq( clone.alpha, layer.alpha, "alpha copied correctly");
+
+        layer.grid = null;
+    }
+
+    function test_11_Layer_Grid_setMap(t) {
+
+        t.plan(2);
+        
+        var options = {tileSize: new OpenLayers.Size(500,50)};
+        var map = new OpenLayers.Map('map', options);
+        layer = new OpenLayers.Layer.Grid(name, url, params);
+
+
+        layer.setMap(map);
+        
+        t.ok( layer.tileSize != null, "tileSize has been set");
+        t.ok( (layer.tileSize.h == 50) && (layer.tileSize.w == 500), "tileSize has been set correctly");
+    }
+
+
+    function test_99_Layer_Grid_destroy (t) {
+
+        t.plan( 3 );
+
+        var map = new OpenLayers.Map('map');
+        layer = new OpenLayers.Layer.Grid(name, url, params);
+        map.addLayer(layer);
+        layer.destroy();
+        t.eq( layer.grid, null, "layer.grid is null after destroy" );
+        t.eq( layer.tileSize, null, "layer.tileSize is null after destroy" );
+
+
+    //test with tile creation
+        layer = new OpenLayers.Layer.WMS(name, url, params);
+        map.addLayer(layer);
+
+        map.setCenter(new OpenLayers.LonLat(0,0), 5);
+
+        //grab a reference to one of the tiles
+        var tile = layer.grid[0][0];        
+
+        layer.destroy();
+
+        t.ok( layer.grid == null, "tiles appropriately destroyed")
+    }
+    
+       // -->
+  </script>
+</head>
+<body>
+<div id="map" style="width:500px;height:550px;display:none"></div>
+</body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_Grid.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_HTTPRequest.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_HTTPRequest.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_HTTPRequest.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,175 @@
+<html>
+<head>
+  <script src="../lib/OpenLayers.js"></script>
+  <script type="text/javascript"><!--
+    var layer; 
+
+    var name = "Test Layer";
+    var url = "http://octo.metacarta.com/cgi-bin/mapserv";
+    var params = { map: '/mapdata/vmap_wms.map', 
+                   layers: 'basic', 
+                   format: 'image/png'};
+    var options = { chicken: 151, foo: "bar" };
+
+    function test_01_Layer_HTTPRequest_constructor (t) {
+        t.plan( 5 );
+
+        layer = new OpenLayers.Layer.HTTPRequest(name, url, params, options);
+        
+        t.ok( layer instanceof OpenLayers.Layer.HTTPRequest, "new OpenLayers.Layer.HTTPRequest returns correctly typed object" );
+
+        // correct bubbling up to Layer.initialize()
+        t.eq( layer.name, name, "layer.name is correct" );
+        t.ok( ((layer.options["chicken"] == 151) && (layer.options["foo"] == "bar")), "layer.options correctly set" );
+
+        // HTTPRequest-specific properties
+        t.eq( layer.url, url, "layer.name is correct" );
+        t.ok( ((layer.params["map"] == '/mapdata/vmap_wms.map') && 
+               (layer.params["layers"] == "basic") &&
+               (layer.params["format"] == "image/png")), "layer.params correctly set" );
+
+
+    }
+
+    function test_02_Layer_HTTPRequest_clone (t) {
+        t.plan( 6 );
+        
+        var toClone = new OpenLayers.Layer.HTTPRequest(name, url, params, options);
+        toClone.chocolate = 5;
+
+        var layer = toClone.clone();
+
+        t.eq(layer.chocolate, 5, "correctly copied randomly assigned property");
+
+        t.ok( layer instanceof OpenLayers.Layer.HTTPRequest, "new OpenLayers.Layer.HTTPRequest returns correctly typed object" );
+
+        // correct bubbling up to Layer.initialize()
+        t.eq( layer.name, name, "layer.name is correct" );
+        t.ok( ((layer.options["chicken"] == 151) && (layer.options["foo"] == "bar")), "layer.options correctly set" );
+
+        // HTTPRequest-specific properties
+        t.eq( layer.url, url, "layer.name is correct" );
+        t.ok( ((layer.params["map"] == '/mapdata/vmap_wms.map') && 
+               (layer.params["layers"] == "basic") &&
+               (layer.params["format"] == "image/png")), "layer.params correctly set" );
+
+    }
+
+    function test_03_Layer_HTTPRequest_setUrl (t) {
+        t.plan( 1 );
+
+        layer = new OpenLayers.Layer.HTTPRequest(name, url, params, options);
+        
+        layer.setUrl("foo");
+        t.eq( layer.url, "foo", "setUrl() works");
+    }
+
+    function test_05_Layer_HTTPRequest_mergeNewParams (t) {
+        t.plan( 3 );
+
+        layer = new OpenLayers.Layer.HTTPRequest(name, url, params, options);
+        
+        var newParams = { layers: 'sooper', 
+                          chickpeas: 'image/png'};
+
+        layer.mergeNewParams(newParams);
+        
+        t.eq( layer.params.layers, "sooper", "mergeNewParams() overwrites well");
+        t.eq( layer.params.chickpeas, "image/png", "mergeNewParams() adds well");
+
+        newParams.chickpeas = 151;
+
+        t.eq( layer.params.chickpeas, "image/png", "mergeNewParams() makes clean copy of hash");
+
+
+    }
+
+    function test_06_Layer_HTTPRequest_getFullRequestString (t) {
+
+        tParams = { layers: 'basic', 
+                   format: 'image/png'};
+        
+        t.plan( 8 );
+
+  // without ?        
+        tUrl = "http://octo.metacarta.com/cgi-bin/mapserv";
+        layer = new OpenLayers.Layer.HTTPRequest(name, tUrl, tParams, null);
+        str = layer.getFullRequestString();
+        t.eq(str, "http://octo.metacarta.com/cgi-bin/mapserv?layers=basic&format=image/png", "getFullRequestString() works for url sans ?");
+
+
+  // with ?        
+        tUrl = "http://octo.metacarta.com/cgi-bin/mapserv?";
+        layer = new OpenLayers.Layer.HTTPRequest(name, tUrl, tParams, null);
+        str = layer.getFullRequestString();
+        t.eq(str, "http://octo.metacarta.com/cgi-bin/mapserv?layers=basic&format=image/png", "getFullRequestString() works for url with ?");
+
+  // with ?param1=5
+        tUrl = "http://octo.metacarta.com/cgi-bin/mapserv?param1=5";
+        layer = new OpenLayers.Layer.HTTPRequest(name, tUrl, tParams, null);
+        str = layer.getFullRequestString();
+        t.eq(str, "http://octo.metacarta.com/cgi-bin/mapserv?param1=5&layers=basic&format=image/png", "getFullRequestString() works for url with ?param1=5");
+
+ 
+  // with ?param1=5&
+        tUrl = "http://octo.metacarta.com/cgi-bin/mapserv?param1=5&";
+        layer = new OpenLayers.Layer.HTTPRequest(name, tUrl, tParams, null);
+        str = layer.getFullRequestString();
+        t.eq(str, "http://octo.metacarta.com/cgi-bin/mapserv?param1=5&layers=basic&format=image/png", "getFullRequestString() works for url with ?param1=5&");
+
+  // passing in new params
+        layer = new OpenLayers.Layer.HTTPRequest(name, tUrl, tParams, null);
+        str = layer.getFullRequestString( { chicken: 6, 
+                                            layers:"road" } );
+        t.eq(str, "http://octo.metacarta.com/cgi-bin/mapserv?param1=5&layers=road&format=image/png&chicken=6", "getFullRequestString() works for passing in new params");
+
+  // layer with null params
+        layer = new OpenLayers.Layer.HTTPRequest(name, tUrl, null, null);
+        str = layer.getFullRequestString();
+        t.eq(str, "http://octo.metacarta.com/cgi-bin/mapserv?param1=5&", "getFullRequestString() works for layer with null params");
+
+  // layer with null params passing in new params
+        layer = new OpenLayers.Layer.HTTPRequest(name, tUrl, null, null);
+        str = layer.getFullRequestString( { chicken: 6, 
+                                            layers:"road" } );
+        t.eq(str, "http://octo.metacarta.com/cgi-bin/mapserv?param1=5&chicken=6&layers=road", "getFullRequestString() works for layer with null params passing in new params");
+
+  // with specified altUrl parameter
+        tUrl = "http://octo.metacarta.com/cgi-bin/mapserv";
+        layer = new OpenLayers.Layer.HTTPRequest(name, "chicken", tParams, null);
+        str = layer.getFullRequestString(null, tUrl);
+        t.eq(str, "http://octo.metacarta.com/cgi-bin/mapserv?layers=basic&format=image/png", "getFullRequestString() works for url sans ?");
+
+    }
+
+    function test_99_Layer_HTTPRequest_destroy (t) {
+        t.plan( 6 );    
+
+        var map = new OpenLayers.Map('map');
+
+        layer = new OpenLayers.Layer.HTTPRequest("Test Layer",
+                                                 "http://www.openlayers.org", 
+                                                 { foo: 2, bar: 3}, 
+                                                 { opt1: 8, opt2: 9});
+
+        map.addLayer(layer);
+        layer.destroy();
+ 
+        // Ensure Layer.destroy() is called
+        t.eq( layer.name, null, "layer.name is null after destroy" );
+        t.eq( layer.div, null, "layer.div is null after destroy" );
+        t.eq( layer.map, null, "layer.map is null after destroy" );
+        t.eq( layer.options, null, "layer.options is null after destroy" );
+ 
+ 
+        // Specific to HTTPRequest 
+        t.eq( layer.url, null, "layer.url is null after destroy" );
+        t.eq( layer.params, null, "layer.params is null after destroy" );
+    }
+  // -->
+  </script>
+</head>
+<body>
+  <div id="map"></div>
+</body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_HTTPRequest.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_Image.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_Image.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_Image.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,96 @@
+<html>
+<head>
+  <script src="../lib/OpenLayers.js"></script>
+  <script type="text/javascript"><!--
+    var layer; 
+
+    function test_01_Layer_Image_constructor (t) {
+        t.plan( 13 );
+        
+        var options = { chicken: 151, foo: "bar", projection: "none" };
+        var layer = new OpenLayers.Layer.Image('Test Layer',
+                                        'http://earthtrends.wri.org/images/maps/4_m_citylights_lg.gif',
+                                                                        new OpenLayers.Bounds(-180, -88.759, 180, 88.759),
+                                                                                                        new OpenLayers.Size(580, 288), options);
+        
+        t.ok( layer instanceof OpenLayers.Layer.Image, "new OpenLayers.Layer.Image returns object" );
+        t.eq( layer.CLASS_NAME, "OpenLayers.Layer.Image", "CLASS_NAME variable set correctly");
+
+        t.eq( layer.name, "Test Layer", "layer.name is correct" );
+        t.ok( layer.id != null, "Layer is given an id");
+        t.ok( layer.projection, "none", "default layer projection correctly set");
+        t.ok( ((layer.chicken == 151) && (layer.foo == "bar")), "layer.options correctly set to Layer Object" );
+        t.ok( ((layer.options["chicken"] == 151) && (layer.options["foo"] == "bar")), "layer.options correctly backed up" );
+
+        options.chicken = 552;
+        
+        t.eq( layer.options["chicken"], 151 , "layer.options correctly made fresh copy" );
+        
+        t.eq( layer.isBaseLayer, true, "Default img layer is base layer" );
+
+        layer = new OpenLayers.Layer.Image('Test Layer',
+                                        'http://earthtrends.wri.org/images/maps/4_m_citylights_lg.gif',
+                                                                        new OpenLayers.Bounds(-180, -88.759, 180, 88.759),
+                                                                                                        new OpenLayers.Size(580, 288));
+        t.ok( layer instanceof OpenLayers.Layer.Image, "new OpenLayers.Layer.Image returns object" );
+        t.eq( layer.name, "Test Layer", "layer.name is correct" );
+        t.ok( layer.projection == null, "default layer projection correctly set");
+        t.ok( layer.options instanceof Object, "layer.options correctly initialized as a non-null Object" );
+    }
+
+    function test_50_Layer_Image_tileTests (t) {
+        t.plan(4);
+        var map = new OpenLayers.Map('map');
+        
+        layer = new OpenLayers.Layer.Image('Test Layer', 
+                                        'http://earthtrends.wri.org/images/maps/4_m_citylights_lg.gif',
+                                                                        new OpenLayers.Bounds(-180, -88.759, 180, 88.759),
+                                                                                                        new OpenLayers.Size(580, 288));
+
+        map.addLayer(layer);
+        map.zoomToMaxExtent();
+        t.eq(layer.tile.position.x,-40, "Tile x positioned correctly at maxextent");
+        t.eq(layer.tile.position.y,107, "Tile y positioned correctly at maxextent");
+        t.eq(layer.tile.imgDiv.src, "http://earthtrends.wri.org/images/maps/4_m_citylights_lg.gif", "URL is correct");
+        map.zoomIn();
+        t.eq(layer.tile.imgDiv.src, "http://earthtrends.wri.org/images/maps/4_m_citylights_lg.gif", "URL is correct");
+    }
+/******
+ * 
+ * 
+ * HERE IS WHERE SOME TESTS SHOULD BE PUT TO CHECK ON THE LONLAT-PX TRANSLATION
+ * FUNCTIONS AND RESOLUTION AND GETEXTENT GETZOOMLEVEL, ETC
+ * 
+ * 
+ */
+
+
+    function test_99_Layer_Image_destroy (t) {
+        t.plan( 4 );    
+
+        var map = new OpenLayers.Map('map');
+        
+        layer = new OpenLayers.Layer.Image('Test Layer', 
+                                        'http://earthtrends.wri.org/images/maps/4_m_citylights_lg.gif',
+                                                                        new OpenLayers.Bounds(-180, -88.759, 180, 88.759),
+                                                                                                        new OpenLayers.Size(580, 288));
+
+        map.addLayer(layer);
+        map.zoomToMaxExtent();
+    
+        layer.destroy();
+
+        t.eq( layer.name, null, "layer.name is null after destroy" );
+        t.eq( layer.div, null, "layer.div is null after destroy" );
+        t.eq( layer.map, null, "layer.map is null after destroy" );
+        t.eq( layer.options, null, "layer.options is null after destroy" );
+
+    }
+  // -->
+  </script>
+</head>
+<body>
+  <div id="map" style="width:500px;height:500px"></div>
+  <div id="map2" style="width:100px;height:100px"></div>
+</body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_Image.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_KaMap.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_KaMap.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_KaMap.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,195 @@
+<html>
+<head>
+  <script src="../lib/OpenLayers.js"></script>
+  <script type="text/javascript"><!--
+    var isMozilla = (navigator.userAgent.indexOf("compatible") == -1);
+    var layer; 
+
+    var name = 'Test Layer';
+    var url = "http://boston.freemap.in/tile.php?";
+    var params = {
+                  'map':'boston-new', 
+                  'g':'border,water,roads,openspace', 
+                  'i':'JPEG'
+                 };
+    var units = "meters";
+
+
+
+    function test_01_Layer_KaMap_constructor (t) {
+        t.plan( 1 );
+                       
+        layer = new OpenLayers.Layer.KaMap(name, url, params, units);
+        t.ok( layer instanceof OpenLayers.Layer.KaMap, "returns OpenLayers.Layer.KaMap object" );
+    }
+
+
+    function test_02_Layer_KaMap_inittiles (t) {
+        t.plan( 2 );
+        var map = new OpenLayers.Map($('map'));
+        layer = new OpenLayers.Layer.KaMap(name, url, params, units);
+        map.addLayer(layer);
+        map.setCenter(new OpenLayers.LonLat(0,0),5);
+        t.eq( layer.grid.length, 6, "KaMap rows is correct." );
+        t.eq( layer.grid[0].length, 4, "KaMap cols is correct." );
+        
+    }
+
+    function test_03_Layer_KaMap_clearTiles (t) {
+        t.plan( 1 );
+        var map = new OpenLayers.Map('map');
+        layer = new OpenLayers.Layer.KaMap(name, url, params, units);
+        map.addLayer(layer);
+
+        map.setCenter(new OpenLayers.LonLat(0,0));
+
+        //grab a reference to one of the tiles
+        var tile = layer.grid[0][0];        
+
+        layer.clearGrid();
+
+        t.ok( layer.grid != null, "layer.grid does not get nullified" );
+    }
+
+
+    function test_04_Layer_KaMap_getKaMapBounds(t) {
+        t.plan( 1 );
+
+        layer = new OpenLayers.Layer.KaMap(name, url, params, units);
+
+        var bl = { bounds: new OpenLayers.Bounds(1,2,0,0)};
+        var tr = { bounds: new OpenLayers.Bounds(0,0,3,4)};
+        layer.grid = [ [6, tr], 
+                       [bl, 7]];
+
+        var bounds = layer.getGridBounds();
+    
+        var testBounds = new OpenLayers.Bounds(1,2,3,4);
+        
+        t.ok( bounds.equals(testBounds), "getKaMapBounds() returns correct bounds")
+        
+        layer.grid = null;
+    }
+
+    function test_05_Layer_KaMap_getResolution(t) {
+        t.plan( 1 );
+
+        var map = new OpenLayers.Map('map');
+        layer = new OpenLayers.Layer.KaMap(name, url, params, units);
+        map.addLayer(layer);
+
+        map.zoom = 5;
+
+        t.eq( layer.getResolution(), 0.0439453125, "getResolution() returns correct value");
+    }
+
+    function test_06_Layer_KaMap_getZoomForExtent(t) {
+        t.plan( 2 );
+        var bounds, zoom;
+
+        var map = new OpenLayers.Map('map');
+        layer = new OpenLayers.Layer.KaMap(name, url, params, units);
+        map.addLayer(layer);
+
+        bounds = new OpenLayers.Bounds(10,10,12,12);
+        zoom = layer.getZoomForExtent(bounds);
+
+        t.eq( zoom, 8, "getZoomForExtent() returns correct value");
+
+        bounds = new OpenLayers.Bounds(10,10,100,100);
+        zoom = layer.getZoomForExtent(bounds);
+
+        t.eq( zoom, 2, "getZoomForExtent() returns correct value");
+    }   
+
+
+    /** THIS WOULD BE WHERE THE TESTS WOULD GO FOR 
+     *     
+     *    -moveTo
+     *    -insertColumn
+     *    -insertRow
+    
+    function 07_Layer_KaMap_moveTo(t) {
+    }
+
+    function 08_Layer_KaMap_insertColumn(t) {
+    }
+
+    function 09_Layer_KaMap_insertRow(t) {
+    }
+
+     * 
+     */
+
+    function test_10_Layer_KaMap_clone(t) {
+        t.plan(4);
+        
+        var options = {tileSize: new OpenLayers.Size(500,50)};
+        var map = new OpenLayers.Map('map', options);
+        layer = new OpenLayers.Layer.KaMap(name, url, params, units);
+        map.addLayer(layer);
+
+        layer.grid = [ [6, 7], 
+                       [8, 9]];
+
+        var clone = layer.clone();
+
+        t.ok( clone.grid != layer.grid, "clone does not copy grid");
+
+        t.ok( clone.tileSize.equals(layer.tileSize), "tileSize correctly cloned");
+
+        layer.tileSize.w += 40;
+
+        t.eq( clone.tileSize.w, 500, "changing layer.tileSize does not change clone.tileSize -- a fresh copy was made, not just copied reference");
+
+        t.eq( clone.alpha, layer.alpha, "alpha copied correctly");
+
+        layer.grid = null;
+    }
+
+    function test_11_Layer_KaMap_setMap(t) {
+
+        t.plan(2);
+        
+        var options = {tileSize: new OpenLayers.Size(500,50)};
+        var map = new OpenLayers.Map('map', options);
+        layer = new OpenLayers.Layer.KaMap(name, url, params, units);
+
+
+        layer.setMap(map);
+        
+        t.ok( layer.tileSize != null, "tileSize has been set");
+        t.ok( (layer.tileSize.h == 50) && (layer.tileSize.w == 500), "tileSize has been set correctly");
+    }
+
+    function test_99_Layer_KaMap_destroy (t) {
+
+        t.plan( 3 );
+
+        var map = new OpenLayers.Map('map');
+        layer = new OpenLayers.Layer.KaMap(name, url, params, units);
+        map.addLayer(layer);
+        layer.destroy();
+        t.eq( layer.grid, null, "layer.grid is null after destroy" );
+        t.eq( layer.tileSize, null, "layer.tileSize is null after destroy" );
+
+
+    //test with tile creation
+        layer = new OpenLayers.Layer.KaMap(name, url, params, units);
+        map.addLayer(layer);
+        map.setCenter(new OpenLayers.LonLat(0,0), 5);
+        //grab a reference to one of the tiles
+        var tile = layer.grid[0][0];        
+
+        layer.destroy();
+
+        t.ok( layer.grid == null, "tiles appropriately destroyed");
+    }
+    
+       // -->
+  </script>
+</head>
+<body>
+<div id="map" style="width:500px;height:550px;display:none"></div>
+</body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_KaMap.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_Markers.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_Markers.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_Markers.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,40 @@
+<html>
+<head>
+  <script src="../lib/OpenLayers.js"></script>
+  <script type="text/javascript"><!--
+    var layer; 
+
+    function test_01_Layer_Markers_constructor (t) {
+        t.plan( 2 );
+        
+        layer = new OpenLayers.Layer.Markers('Test Layer');
+        t.ok( layer instanceof OpenLayers.Layer.Markers, "new OpenLayers.Layer.Markers returns object" );
+        t.eq( layer.name, "Test Layer", "layer.name is correct" );
+    }
+    function test_02_Layer_Markers_addlayer (t) {
+        t.plan( 3 );
+        
+        layer = new OpenLayers.Layer.Markers('Test Layer');
+        t.ok( layer instanceof OpenLayers.Layer.Markers, "new OpenLayers.Layer.Markers returns object" );
+        t.eq( layer.name, "Test Layer", "layer.name is correct" );
+        layer.addMarker(new OpenLayers.Marker(new OpenLayers.LonLat(0,0), 
+                                              new OpenLayers.Icon())
+                       );
+        t.eq( layer.markers.length, 1, "addLayer adds marker to layer." );
+    }
+
+    function test_99_Layer_Markers_destroy (t) {
+        t.plan( 1 );    
+        layer = new OpenLayers.Layer.Markers('Test Layer');
+        var map = new OpenLayers.Map('map');
+        map.addLayer(layer);
+        layer.destroy();
+        t.eq( layer.map, null, "layer.map is null after destroy" );
+    }
+  // -->
+  </script>
+</head>
+<body>
+  <div id="map"></div>
+</body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_Markers.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_MultiMap.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_MultiMap.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_MultiMap.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,128 @@
+<html>
+<head>
+      <script type="text/javascript" src="http://clients.multimap.com/API/maps/1.1/metacarta_04"></script>
+  <script src="../lib/OpenLayers.js"></script>
+  <script type="text/javascript"><!--
+    var layer; 
+
+    function test_01_Layer_MultiMap_constructor (t) {
+        t.plan( 4 );
+        
+        var map = new OpenLayers.Map('map');
+        var layer = new OpenLayers.Layer.MultiMap('Goog Layer');
+        map.addLayer(layer);
+        
+        
+        t.ok( layer instanceof OpenLayers.Layer.MultiMap, "new OpenLayers.Layer.MultiMap returns object" );
+        t.eq( layer.CLASS_NAME, "OpenLayers.Layer.MultiMap", "CLASS_NAME variable set correctly");
+
+        t.eq( layer.name, "Goog Layer", "layer.name is correct" );
+
+        t.ok ( layer.multimap != null, "MultiMap Object correctly loaded");
+    }
+
+    function test_02_Layer_MultiMap_isBaseLayer (t) {
+        t.plan(1);
+        
+        var layer = new OpenLayers.Layer.MultiMap('Goog Layer');
+
+        t.ok(layer.isBaseLayer, "a default load of google layer responds as a base layer");
+    }    
+
+    function test_03_Layer_MultiMap_Translation_zoom (t) {
+        
+        t.plan( 4 );
+        
+        var map = new OpenLayers.Map('map');
+        var layer = new OpenLayers.Layer.MultiMap('Goog Layer');
+        map.addLayer(layer);
+
+     // these two lines specify an appropriate translation. 
+     //  the code afterwards works by itself to test that translation
+     //  works correctly both ways.
+        var gZoom = 5;
+        var correspondingOLZoom = 4;
+
+
+        olZoom = layer.getOLZoomFromMMZoom(gZoom);
+        t.eq(olZoom, correspondingOLZoom, "Translation from GZoom to OL Zoom works");
+        t.eq(layer.getMMZoomFromOLZoom(olZoom), gZoom, "Translation from OL Zoom to GZoom works");
+
+        t.ok( layer.getMMZoomFromOLZoom(null) == null, "getGZoomFromOLZoom(null) returns null");
+        t.ok( layer.getOLZoomFromMMZoom(null) == null, "getOLZoomFromGZoom(null) returns null");
+    }
+
+    function test_04_Layer_MultiMap_Translation_lonlat (t) {
+        
+        t.plan( 4 );
+        
+        var map = new OpenLayers.Map('map');
+        var layer = new OpenLayers.Layer.MultiMap('Goog Layer');
+        map.addLayer(layer);
+
+     // these two lines specify an appropriate translation. 
+     //  the code afterwards works by itself to test that translation
+     //  works correctly both ways.
+        var gLatLng = new MMLatLon(50,100);
+        var correspondingOLLonLat = new OpenLayers.LonLat(100,50);
+
+
+        olLonLat = layer.getOLLonLatFromMMLatLong(gLatLng);
+        t.ok(olLonLat.equals(correspondingOLLonLat), "Translation from GLatLng to OpenLayers.LonLat works");
+
+        var transGLatLng = layer.getMMLatLongFromOLLonLat(olLonLat);
+        t.ok( (transGLatLng.lat == gLatLng.lat) && (transGLatLng.lon == transGLatLng.lon), "Translation from OpenLayers.LonLat to GLatLng works");
+
+        t.ok( layer.getMMLatLongFromOLLonLat(null) == null, "getGLatLngFromOLLonLat(null) returns null");
+        t.ok( layer.getOLLonLatFromMMLatLong(null) == null, "getOLLonLatFromGLatLng(null) returns null");
+    }
+
+    function test_05_Layer_MultiMap_Translation_pixel (t) {
+        
+        t.plan( 4 );
+        
+        var map = new OpenLayers.Map('map');
+        var layer = new OpenLayers.Layer.MultiMap('Goog Layer');
+        map.addLayer(layer);
+
+     // these two lines specify an appropriate translation. 
+     //  the code afterwards works by itself to test that translation
+     //  works correctly both ways.
+        var gPoint = new MMPoint(50,100);
+        var correspondingOLPixel = new OpenLayers.Pixel(50, 100);
+
+
+        olPixel = layer.getOLPixelFromPixel(gPoint);
+        t.ok( olPixel.equals(correspondingOLPixel), "Translation from GPoint to OpenLayers.Pixel works");
+
+        var transGPoint = layer.getPixelFromOLPixel(olPixel);
+        t.ok( ((transGPoint.x == transGPoint.x) && (transGPoint.y == transGPoint.y)), "Translation from OpenLayers.Pixel to GPoint works");
+
+        t.ok( layer.getPixelFromOLPixel(null) == null, "getGPointFromOLPixel(null) returns null");
+        t.ok( layer.getOLPixelFromPixel(null) == null, "getOLPixelFromGPoint(null) returns null");
+    }
+
+
+    function test_99_Layer_destroy (t) {
+        t.plan( 5 );    
+
+        var map = new OpenLayers.Map('map');
+        
+        layer = new OpenLayers.Layer.MultiMap('Test Layer');
+        map.addLayer(layer);
+
+        layer.destroy();
+
+        t.eq( layer.name, null, "layer.name is null after destroy" );
+        t.eq( layer.div, null, "layer.div is null after destroy" );
+        t.eq( layer.map, null, "layer.map is null after destroy" );
+        t.eq( layer.options, null, "layer.options is null after destroy" );
+        t.eq( layer.multimap, null, "layer.gmap is null after destroy" );
+    }
+  // -->
+  </script>
+</head>
+<body>
+  <div id="map"></div>
+</body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_MultiMap.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_Text.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_Text.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_Text.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,83 @@
+<html>
+<head>
+  <script src="../lib/OpenLayers.js"></script>
+  <script type="text/javascript"><!--
+    var isMozilla = (navigator.userAgent.indexOf("compatible") == -1);
+    var layer; 
+
+    function test_01_Layer_Text_constructor (t) {
+        t.plan( 5 );
+        
+        layer = new OpenLayers.Layer.Text('Test Layer', { location: 'data_Layer_Text_textfile.txt'});
+        t.ok( layer instanceof OpenLayers.Layer.Text, "new OpenLayers.Layer.Text returns object" );
+        t.eq( layer.location, "data_Layer_Text_textfile.txt", "layer.location is correct" );
+        var markers;
+        t.delay_call( 1, function() {  
+            t.eq( layer.markers.length, 2, "marker length is correct" );
+            var ll = new OpenLayers.LonLat(20, 10);
+            t.ok( layer.markers[0].lonlat.equals(ll), "first marker is correct" );
+            t.eq( layer.markers[0].icon.url, 'http://boston.openguides.org/markers/ORANGE.png', "icon" );
+        } );
+    }
+    function test_02_Layer_Text_draw (t) { 
+//        t.plan(5);
+        t.plan( 2 );
+        layer = new OpenLayers.Layer.Text('Test Layer', { location: 'data_Layer_Text_textfile.txt'});
+        t.ok( layer instanceof OpenLayers.Layer.Text, "new OpenLayers.Layer.Text returns object" );
+        var map = new OpenLayers.Map('map');
+        var baseLayer = new OpenLayers.Layer.WMS("Test Layer", 
+            "http://octo.metacarta.com/cgi-bin/mapserv?",
+            {map: "/mapdata/vmap_wms.map", layers: "basic"});
+        map.addLayer(baseLayer);
+        map.addLayer(layer);
+        t.eq( map.layers[1].name, layer.name, "Layer added to map okay" );
+        t.delay_call( 1, function() { 
+          map.setCenter(new OpenLayers.LonLat(0,0),0);
+
+/*
+            if (!isMozilla)
+                t.ok( true, "skipping element test outside of Mozilla");
+            else
+              t.ok( map.layers[0].div.firstChild instanceof HTMLImageElement, "Marker added to div" )
+
+          t.eq( map.layers[0].div.firstChild.style.top, "219px", "Marker top set correctly" )
+          t.eq( map.layers[0].div.firstChild.style.left, "273px", "Marker left set correctly" )
+*/
+        });;
+    }
+    function test_03_Layer_Text_events (t) {
+        t.plan( 4 );    
+        layer = new OpenLayers.Layer.Text('Test Layer', { location: 'data_Layer_Text_textfile.txt'});
+        var map = new OpenLayers.Map('map');
+        var baseLayer = new OpenLayers.Layer.WMS("Test Layer", 
+            "http://octo.metacarta.com/cgi-bin/mapserv?",
+            {map: "/mapdata/vmap_wms.map", layers: "basic"});
+        map.addLayer(baseLayer);
+        map.addLayer(layer);
+        map.setCenter(new OpenLayers.LonLat(0,0),0);
+        var event = {};
+        t.delay_call( 1, function() {  
+          t.ok(layer.markers[0].events, "First marker has an events object");
+          t.eq(layer.markers[0].events.listeners['click'].length, 1, "Marker events has one object");
+          layer.markers[0].events.triggerEvent('click', event);
+          t.eq(map.popups.length, 1, "Popup opened correctly");
+          layer.markers[1].events.triggerEvent('click', event);
+          t.eq(map.popups.length, 1, "1st popup gone, 2nd Popup opened correctly");
+        });
+    }
+
+    function test_99_Layer_Text_destroy (t) {
+        t.plan( 1 );    
+        layer = new OpenLayers.Layer.Text('Test Layer');
+        var map = new OpenLayers.Map('map');
+        map.addLayer(layer);
+        layer.destroy();
+        t.eq( layer.map, null, "layer.map is null after destroy" );
+    }
+  // -->
+  </script>
+</head>
+<body>
+  <div id="map" style="width:500px; height:500px"></div>
+</body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_Text.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_WMS.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_WMS.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_WMS.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,223 @@
+<html>
+<head>
+    <script src='http://maps.google.com/maps?file=api&amp;v=2&amp;key=ABQIAAAAjpkAC9ePGem0lIq5XcMiuhR_wWLPFku8Ix9i2SXYRVK3e45q1BQUd_beF8dtzKET_EteAjPdGDwqpQ'></script>
+<script src="../lib/OpenLayers.js"></script>
+  <script type="text/javascript"><!--
+    var isMozilla = (navigator.userAgent.indexOf("compatible") == -1);
+    var layer; 
+
+    var name = 'Test Layer';
+    var url = "http://octo.metacarta.com/cgi-bin/mapserv";
+    var params = { map: '/mapdata/vmap_wms.map', 
+                   layers: 'basic', 
+                   format: 'image/png'};
+
+    function test_01_Layer_WMS_constructor (t) {
+        t.plan( 4 );
+
+        var url = "http://octo.metacarta.com/cgi-bin/mapserv";
+        layer = new OpenLayers.Layer.WMS(name, url, params);
+        t.ok( layer instanceof OpenLayers.Layer.WMS, "new OpenLayers.Layer.WMS returns object" );
+        t.eq( layer.url, "http://octo.metacarta.com/cgi-bin/mapserv", "layer.url is correct (HTTPRequest inited)" );
+        t.eq( layer.params.MAP, "/mapdata/vmap_wms.map", "params passed in correctly uppercased" );
+
+        t.eq( layer.params.SERVICE, "WMS", "default params correclty uppercased and copied");
+
+
+    }
+    
+    function test_02_Layer_WMS_addtile (t) {
+        t.plan( 6 );
+    
+        var url = "http://octo.metacarta.com/cgi-bin/mapserv";
+        layer = new OpenLayers.Layer.WMS(name, url, params);
+        var map = new OpenLayers.Map($('map'));
+        map.addLayer(layer);
+        var pixel = new OpenLayers.Pixel(5,6);
+        var tile = layer.addTile(new OpenLayers.Bounds(1,2,3,4), pixel);
+        tile.draw();
+
+        var img = tile.imgDiv;
+
+        t.eq( img.src, "http://octo.metacarta.com/cgi-bin/mapserv?MAP=/mapdata/vmap_wms.map&LAYERS=basic&FORMAT=image/png&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&STYLES=&EXCEPTIONS=application/vnd.ogc.se_inimage&SRS=EPSG:4326&BBOX=1,2,3,4&WIDTH=256&HEIGHT=256", "image src is created correctly via addtile" );
+        t.eq( tile.imgDiv.style.top, "6px", "image top is set correctly via addtile" );
+        t.eq( tile.imgDiv.style.left, "5px", "image top is set correctly via addtile" );
+
+        var firstChild = layer.div.firstChild;
+        if (!isMozilla)
+            t.ok( true, "skipping element test outside of Mozilla");
+        else
+            t.ok( firstChild instanceof HTMLElement, "div first child is an image object" );
+        t.eq( firstChild.src, "http://octo.metacarta.com/cgi-bin/mapserv?MAP=/mapdata/vmap_wms.map&LAYERS=basic&FORMAT=image/png&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&STYLES=&EXCEPTIONS=application/vnd.ogc.se_inimage&SRS=EPSG:4326&BBOX=1,2,3,4&WIDTH=256&HEIGHT=256", "div first child is correct image object" );
+        t.eq( tile.position.toString(), "x=5,y=6", "Position of tile is set correctly." );
+    }
+    
+    function test_03_Layer_WMS_inittiles (t) {
+        t.plan( 2 );
+        var map = new OpenLayers.Map($('map'));
+        layer = new OpenLayers.Layer.WMS(name, url, params);
+        map.addLayer(layer);
+        map.setCenter(new OpenLayers.LonLat(0,0),5);
+        t.eq( layer.grid.length, 7, "Grid rows is correct." );
+        t.eq( layer.grid[0].length, 6, "Grid cols is correct." );
+        
+    }
+
+
+    function test_04_Layer_WMS_clone (t) {
+        t.plan(4);
+        
+        var url = "http://octo.metacarta.com/cgi-bin/mapserv";
+        var options = {tileSize: new OpenLayers.Size(500,50)};
+        var map = new OpenLayers.Map('map', options);
+        layer = new OpenLayers.Layer.WMS(name, url, params);
+        map.addLayer(layer);
+
+        layer.grid = [ [6, 7], 
+                       [8, 9]];
+
+        var clone = layer.clone();
+
+        t.ok( clone.grid != layer.grid, "clone does not copy grid");
+
+        t.ok( clone.tileSize.equals(layer.tileSize), "tileSize correctly cloned");
+
+        layer.tileSize.w += 40;
+
+        t.eq( clone.tileSize.w, 500, "changing layer.tileSize does not change clone.tileSize -- a fresh copy was made, not just copied reference");
+
+        t.eq( clone.alpha, layer.alpha, "alpha copied correctly");
+
+        layer.grid = null;
+    }
+
+    function test_05_Layer_WMS_isBaseLayer(t) {
+        t.plan(3);
+        
+        var url = "http://octo.metacarta.com/cgi-bin/mapserv";
+        layer = new OpenLayers.Layer.WMS(name, url, params);
+        t.ok( layer.isBaseLayer, "baselayer is true by default");
+
+        var newParams = OpenLayers.Util.extend(new Object(), params);
+        newParams.transparent = "true";
+        layer = new OpenLayers.Layer.WMS(name, url, newParams);
+        t.ok( !layer.isBaseLayer, "baselayer is false when transparent is set to true");
+
+        layer = new OpenLayers.Layer.WMS(name, url, params, {isBaseLayer: false});
+        t.ok( !layer.isBaseLayer, "baselayer is false when option is set to false" );
+    }
+
+    function test_06_Layer_WMS_mergeNewParams (t) {
+        t.plan( 3 );
+
+        var url = "http://octo.metacarta.com/cgi-bin/mapserv";
+        layer = new OpenLayers.Layer.WMS(name, url, params);
+        
+        var newParams = { layers: 'sooper', 
+                          chickpeas: 'image/png'};
+
+        layer.mergeNewParams(newParams);
+        
+        t.eq( layer.params.LAYERS, "sooper", "mergeNewParams() overwrites well");
+        t.eq( layer.params.CHICKPEAS, "image/png", "mergeNewParams() adds well");
+
+        newParams.CHICKPEAS = 151;
+
+        t.eq( layer.params.CHICKPEAS, "image/png", "mergeNewParams() makes clean copy of hashtable");
+    }
+
+    function test_07_Layer_WMS_getFullRequestString (t) {
+
+        
+        t.plan( 2 );
+
+        var map = new OpenLayers.Map('map');
+        map.projection = "xx";
+        tUrl = "http://octo.metacarta.com/cgi-bin/mapserv";
+        tParams = { layers: 'basic', 
+                   format: 'image/png'};
+        var tLayer = new OpenLayers.Layer.WMS(name, tUrl, tParams);
+        map.addLayer(tLayer);
+        str = tLayer.getFullRequestString();
+        t.eq(str, "http://octo.metacarta.com/cgi-bin/mapserv?LAYERS=basic&FORMAT=image/png&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&STYLES=&EXCEPTIONS=application/vnd.ogc.se_inimage&SRS=xx", "getFullRequestString() adds SRS value");
+ 
+        tLayer.projection = "none";
+        str = tLayer.getFullRequestString();
+        t.eq(str, "http://octo.metacarta.com/cgi-bin/mapserv?LAYERS=basic&FORMAT=image/png&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&STYLES=&EXCEPTIONS=application/vnd.ogc.se_inimage", "getFullRequestString() by default does *not* add SRS value if projection is 'none'");
+ 
+    }
+
+    function test_08_Layer_WMS_setOpacity (t) {
+        t.plan( 5 );
+
+        var map = new OpenLayers.Map('map');
+        map.projection = "xx";
+        tUrl = "http://octo.metacarta.com/cgi-bin/mapserv";
+        tParams = { layers: 'basic', 
+                   format: 'image/png'};
+        tOptions = { 'opacity': '0.5' };           
+        var tLayer = new OpenLayers.Layer.WMS(name, tUrl, tParams, tOptions);
+        map.addLayer(tLayer);
+        map.zoomToMaxExtent();
+        t.eq(tLayer.opacity, "0.5", "Opacity is set correctly");
+        t.eq(tLayer.div.firstChild.style.opacity, "0.5", "Opacity on tile is correct");
+        tLayer.setOpacity("0.6");
+        t.eq(tLayer.opacity, "0.6", "setOpacity works properly");
+        t.eq(tLayer.div.firstChild.style.opacity, "0.6", "Opacity on tile is changed correctly");
+        var pixel = new OpenLayers.Pixel(5,6);
+        var tile = tLayer.addTile(new OpenLayers.Bounds(1,2,3,4), pixel);
+        tile.draw();
+        t.eq(tile.imgDiv.style.opacity, "0.6", "Tile opacity is set correctly");
+
+    }    
+    
+    function test_20_Layer_WMS_Reproject (t) { 
+        t.plan(5);
+        var map = new OpenLayers.Map('map');
+        var layer = new OpenLayers.Layer.Google("Google");
+        map.addLayer(layer);
+        layer = new OpenLayers.Layer.WMS(name, url, params, {isBaseLayer: false});
+        layer.isBaseLayer=false;
+        map.addLayer(layer);
+        map.setCenter(new OpenLayers.LonLat(0,0), 5);
+        var tile = layer.grid[0][0];
+        t.eq( tile.bounds.left, -22.5, "left side matches" ); 
+        t.eq( tile.bounds.right, -11.25, "top side matches" ); 
+        t.eq( tile.bounds.bottom.toFixed(6), '11.178402', "bottom side matches" ); 
+        t.eq( tile.bounds.top.toFixed(6), '21.943046', "top side matches" ); 
+        
+        var map = new OpenLayers.Map('map');
+        layer = new OpenLayers.Layer.WMS(name, url, params);
+        map.addLayer(layer);
+        map.setCenter(new OpenLayers.LonLat(0,0), 5);
+        var tile = layer.grid[0][0];
+        t.ok( tile.bounds.equals(new OpenLayers.Bounds(-33.75, 33.75, -22.5, 45)), "okay");
+    }
+    
+    function test_99_Layer_WMS_destroy (t) {
+
+        t.plan( 1 );
+
+        var map = new OpenLayers.Map('map');
+        layer = new OpenLayers.Layer.WMS(name, url, params);
+        map.addLayer(layer);
+
+        map.setCenter(new OpenLayers.LonLat(0,0), 5);
+
+        //grab a reference to one of the tiles
+        var tile = layer.grid[0][0];        
+
+        layer.destroy();
+        
+    // checks to make sure superclass (grid) destroy() was called    
+        
+        t.ok( layer.grid == null, "grid set to null");
+    }
+    
+  // -->
+  </script>
+</head>
+<body>
+<div id="map" style="width:500px;height:550px"></div>
+</body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Layer_WMS.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_LonLat.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_LonLat.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_LonLat.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,96 @@
+<html>
+<head>
+  <script src="../lib/OpenLayers.js"></script>
+  <script type="text/javascript"><!--
+
+    var lonlat; 
+
+    function test_01_LonLat_constructor (t) {
+        t.plan( 4 );
+        lonlat = new OpenLayers.LonLat(6, 5);
+        t.ok( lonlat instanceof OpenLayers.LonLat, "new OpenLayers.LonLat returns LonLat object" );
+        t.eq( lonlat.CLASS_NAME, "OpenLayers.LonLat", "lonlat.CLASS_NAME is set correctly");
+        t.eq( lonlat.lon, 6, "lonlat.lon is set correctly");
+        t.eq( lonlat.lat, 5, "lonlat.lat is set correctly");
+    }
+    
+    function test_01a_LonLat_constructorFromStrings (t) {
+        t.plan( 4 );
+        lonlat = new OpenLayers.LonLat("6", "5");
+        t.ok( lonlat instanceof OpenLayers.LonLat, "new OpenLayers.LonLat returns LonLat object" );
+        t.eq( lonlat.CLASS_NAME, "OpenLayers.LonLat", "lonlat.CLASS_NAME is set correctly");
+        t.eq( lonlat.lon, 6, "lonlat.lon is set correctly");
+        t.eq( lonlat.lat, 5, "lonlat.lat is set correctly");
+    }
+
+    function test_02_LonLat_toString(t) {
+        t.plan( 1 );
+        lonlat = new OpenLayers.LonLat(5,6);
+        t.eq( lonlat.toString(), "lon=5,lat=6", "lonlat.toString() returns correctly");
+    }
+
+    function test_02A_LonLat_toShortString(t) {
+        t.plan( 1 );
+        lonlat = new OpenLayers.LonLat(5,6);
+        t.eq( lonlat.toShortString(), "5, 6", "lonlat.toShortString() returns correctly");
+    }
+
+    function test_03_LonLat_clone(t) {
+        t.plan( 3 );
+        oldLonLat = new OpenLayers.LonLat(5,6);
+        lonlat = oldLonLat.clone();
+        t.ok( lonlat instanceof OpenLayers.LonLat, "clone returns new OpenLayers.LonLat object" );
+        t.ok( lonlat.equals(oldLonLat), "lonlat is set correctly");
+        
+        oldLonLat.lon = 100;
+        t.eq( lonlat.lon, 5, "changing oldLonLat.lon doesn't change lonlat.lon");
+    }
+
+    function test_04_LonLat_add(t) {
+        t.plan( 2 );
+
+        lonlatA = new OpenLayers.LonLat(10,100);
+
+        addpx = lonlatA.add(5, 50);
+        var ll = new OpenLayers.LonLat(10,100);
+        t.ok( lonlatA.equals(ll), "lonlatA is not modified by add operation");
+
+        var ll = new OpenLayers.LonLat(15,150);
+        t.ok( addpx.equals(ll), "addpx is set correctly");
+    }
+    
+    function test_06_LonLat_equals(t) {
+        t.plan( 5 );
+        lonlat = new OpenLayers.LonLat(5,6);
+
+        ll = new OpenLayers.LonLat(5,6);
+        t.eq( lonlat.equals(ll), true, "(5,6) equals (5,6)");
+
+        ll = new OpenLayers.LonLat(1,6);
+        t.eq( lonlat.equals(ll), false, "(5,6) does not equal (1,6)");
+
+        ll = new OpenLayers.LonLat(5,2);
+        t.eq( lonlat.equals(ll), false, "(5,6) does not equal (5,2)");
+
+        ll = new OpenLayers.LonLat(1,2);
+        t.eq( lonlat.equals(ll), false, "(5,6) does not equal (1,2)");
+
+        t.ok( !lonlat.equals(null), "equals() returns false on comparison to null");
+
+    }
+
+    function test_07_LonLat_fromString(t) {
+        t.plan( 2 );
+        lonlat = OpenLayers.LonLat.fromString("6,5");
+        t.ok( lonlat instanceof OpenLayers.LonLat, "new OpenLayers.LonLat returns LonLat object" );
+
+        var ll = new OpenLayers.LonLat(6, 5);
+        t.ok( lonlat.equals(ll), "lonlat is set correctly");
+    }
+
+  // -->
+  </script>
+</head>
+<body>
+</body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_LonLat.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Map.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Map.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Map.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,319 @@
+<html>
+<head>
+  <script src="../lib/OpenLayers.js"></script>
+  <script type="text/javascript"><!--
+    var isMozilla = (navigator.userAgent.indexOf("compatible") == -1);
+    var map; 
+    function test_01_Map_constructor (t) {
+        t.plan( 9 );
+        
+        map = new OpenLayers.Map('map'); // no longer need to call $(), constructor does it
+        var baseLayer = new OpenLayers.Layer.WMS("Test Layer", 
+            "http://octo.metacarta.com/cgi-bin/mapserv?",
+            {map: "/mapdata/vmap_wms.map", layers: "basic"});
+        map.addLayer(baseLayer);
+
+        t.ok( map instanceof OpenLayers.Map, "new OpenLayers.Map returns object" );
+        if (!isMozilla) {
+            t.ok( true, "skipping element test outside of Mozilla");
+            t.ok( true, "skipping element test outside of Mozilla");
+            t.ok( true, "skipping element test outside of Mozilla");
+        } else {
+            t.ok( map.div instanceof HTMLDivElement, "map.div is an HTMLDivElement" );
+            t.ok( map.viewPortDiv instanceof HTMLDivElement, "map.viewPortDiv is an HTMLDivElement" );
+            t.ok( map.layerContainerDiv instanceof HTMLDivElement, "map.layerContainerDiv is an HTMLDivElement" );
+        }
+        t.ok( map.layers instanceof Array, "map.layers is an Array" );
+        t.ok( map.controls instanceof Array, "map.controls is an Array" );
+        t.ok( map.events instanceof OpenLayers.Events, "map.events is an OpenLayers.Events" );
+        t.ok( map.getMaxExtent() instanceof OpenLayers.Bounds, "map.maxExtent is an OpenLayers.Bounds" );
+        t.ok( map.getNumZoomLevels() > 0, "map has a default numZoomLevels" );
+    }
+    function test_02_Map_center(t) {
+        t.plan(3);
+        map = new OpenLayers.Map($('map'));
+        var baseLayer = new OpenLayers.Layer.WMS("Test Layer", 
+            "http://octo.metacarta.com/cgi-bin/mapserv?",
+            {map: "/mapdata/vmap_wms.map", layers: "basic"});
+        map.addLayer(baseLayer);
+        var ll = new OpenLayers.LonLat(2,1);
+        map.setCenter(ll, 0);
+        t.ok( map.getCenter() instanceof OpenLayers.LonLat, "map.getCenter returns a LonLat");
+        t.eq( map.getZoom(), 0, "map.zoom is correct after calling setCenter");
+        t.ok( map.getCenter().equals(ll), "map center is correct after calling setCenter");
+    }
+    function test_03_Map_add_layers(t) {
+        t.plan(6);
+        map = new OpenLayers.Map($('map'));
+        var layer1 = new OpenLayers.Layer.WMS("Layer 1", 
+            "http://octo.metacarta.com/cgi-bin/mapserv?",
+            {map: "/mapdata/vmap_wms.map", layers: "basic"});
+        var layer2 = new OpenLayers.Layer.WMS("Layer 2",
+            "http://wms.jpl.nasa.gov/wms.cgi", {layers: "modis,global_mosaic"});
+        // this uses map.addLayer internally
+        map.addLayers([layer1, layer2])
+        t.eq( map.layers.length, 2, "map has exactly two layers" );
+        t.ok( map.layers[0] === layer1, "1st layer is layer1" );
+        t.ok( map.layers[1] === layer2, "2nd layer is layer2" );
+        t.ok( layer1.map === map, "layer.map is map" ); 
+        t.eq( parseInt(layer1.div.style.zIndex), map.Z_INDEX_BASE['BaseLayer'], 
+                "layer1 zIndex is set" );
+        t.eq( parseInt(layer2.div.style.zIndex), map.Z_INDEX_BASE['BaseLayer'] + 5, 
+                "layer2 zIndex is set" );
+    }
+    function test_04_Map_options(t) {
+        t.plan(3);
+        map = new OpenLayers.Map($('map'), {numZoomLevels: 6, maxResolution: 3.14159, theme: 'foo'});
+        t.eq( map.numZoomLevels, 6, "map.numZoomLevels set correctly via options hashtable" );
+        t.eq( map.maxResolution, 3.14159, "map.maxResolution set correctly via options hashtable" );
+        t.eq( map.theme, 'foo', "map theme set correctly." );
+    }
+    function test_05_Map_center(t) {
+        t.plan(4);
+        map = new OpenLayers.Map($('map'));
+        var baseLayer = new OpenLayers.Layer.WMS("Test Layer", 
+            "http://octo.metacarta.com/cgi-bin/mapserv?",
+            {map: "/mapdata/vmap_wms.map", layers: "basic"} );
+        map.addLayer(baseLayer);
+        var ll = new OpenLayers.LonLat(2,1);
+        map.setCenter(ll, 0);
+        map.zoomIn();
+        t.eq( map.getZoom(), 1, "map.zoom is correct after calling setCenter,zoom in");
+        t.ok( map.getCenter().equals(ll), "map center is correct after calling setCenter, zoom in");
+        map.zoomOut();
+        t.eq( map.getZoom(), 0, "map.zoom is correct after calling setCenter,zoom in, zoom out");
+
+    map.zoomTo(5);
+    t.eq( map.getZoom(), 5, "map.zoom is correct after calling zoomTo" );
+/**
+    map.zoomToMaxExtent();
+    t.eq( map.getZoom(), 2, "map.zoom is correct after calling zoomToMaxExtent" );
+    var lonlat = map.getCenter();
+    var zero = new OpenLayers.LonLat(0, 0);
+    t.ok( lonlat.equals(zero), "map center is correct after calling zoomToFullExtent" );
+
+    */
+    }
+
+    function test_06_Map_zoomend_event (t) {
+        t.plan(2);
+        map = new OpenLayers.Map('map');
+        var baseLayer = new OpenLayers.Layer.WMS("Test Layer", 
+            "http://octo.metacarta.com/cgi-bin/mapserv?",
+            {map: "/mapdata/vmap_wms.map", layers: "basic"});
+        map.addLayer(baseLayer);
+        map.events.register("zoomend", {count: 0}, function() { 
+            this.count++; 
+            t.ok(true, "zoomend event was triggered " + this.count + " times");
+            });
+        map.setCenter(new OpenLayers.LonLat(2, 1), 0);
+        map.zoomIn();
+        map.zoomOut();
+    }
+    
+    function test_07_Map_add_remove_popup (t) {
+        t.plan(4);
+
+        map = new OpenLayers.Map('map');
+        var baseLayer = new OpenLayers.Layer.WMS("Test Layer", 
+            "http://octo.metacarta.com/cgi-bin/mapserv?",
+            {map: "/mapdata/vmap_wms.map", layers: "basic"});
+        map.addLayer(baseLayer);
+        
+        var popup = new OpenLayers.Popup("chicken", 
+                                         new OpenLayers.LonLat(0,0),
+                                         new OpenLayers.Size(200,200));
+        map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+
+        map.addPopup(popup);
+        var pIndex = OpenLayers.Util.indexOf(map.popups, popup);
+        t.eq(pIndex, 0, "popup successfully added to Map's internal popups array");
+
+        var nodes = map.layerContainerDiv.childNodes;
+        
+        var found = false;
+        for (var i=0; i < nodes.length; i++) {
+            if (nodes.item(i) == popup.div) {
+                found = true;
+                break;
+            }
+        }
+        t.ok(found, "popup.div successfully added to the map's viewPort");
+
+        
+        map.removePopup(popup);
+        var pIndex = OpenLayers.Util.indexOf(map.popups, popup);
+        t.eq(pIndex, -1, "popup successfully removed from Map's internal popups array");
+
+        var found = false;
+        for (var i=0; i < nodes.length; i++) {
+            if (nodes.item(i) == popup.div) {
+                found = true;
+                break;
+            }
+        }
+        t.ok(!found, "popup.div successfully removed from the map's viewPort");
+    }
+/***  THIS IS A GOOD TEST, BUT IT SHOULD BE MOVED TO WMS. 
+ *     Also, it won't work until we figure out the viewSize bug
+
+    function 08_Map_px_lonlat_translation (t) {
+        t.plan( 6 );    
+        map = new OpenLayers.Map($('map'));
+        var baseLayer = new OpenLayers.Layer.WMS("Test Layer", 
+            "http://octo.metacarta.com/cgi-bin/mapserv?",
+            {map: "/mapdata/vmap_wms.map", layers: "basic"});
+        map.addLayer(baseLayer);
+        map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+ 
+        var pixel = new OpenLayers.Pixel(50,150);
+        var lonlat = map.getLonLatFromViewPortPx(pixel);
+        t.ok( lonlat instanceof OpenLayers.LonLat, "getLonLatFromViewPortPx returns valid OpenLayers.LonLat" );
+
+        var newPixel = map.getViewPortPxFromLonLat(lonlat);
+        t.ok( newPixel instanceof OpenLayers.Pixel, "getViewPortPxFromLonLat returns valid OpenLayers.Pixel" );
+
+        // WARNING!!!  I'm faily sure that the following test's validity 
+        //             depends highly on rounding and the resolution. For now,
+        //             in the default case, it seems to work. This may not 
+        //             always be so.
+        t.ok( newPixel.equals(pixel), "Translation to pixel and back to lonlat is consistent");
+
+        lonlat = map.getLonLatFromPixel(pixel);
+        t.ok( lonlat instanceof OpenLayers.LonLat, "getLonLatFromPixel returns valid OpenLayers.LonLat" );
+
+        newPixel = map.getPixelFromLonLat(lonlat);
+        t.ok( newPixel instanceof OpenLayers.Pixel, "getPixelFromLonLat returns valid OpenLayers.Pixel" );
+
+        t.ok( newPixel.equals(pixel), "2nd translation to pixel and back to lonlat is consistent");
+    }
+ */
+    function test_09_Map_isValidLonLat(t) {
+        t.plan( 3 );    
+        map = new OpenLayers.Map($('map'));
+        layer = new OpenLayers.Layer.WMS('Test Layer', 
+                    "http://octo.metacarta.com/cgi-bin/mapserv", 
+                    {map: '/mapdata/vmap_wms.map', layers: 'basic', format: 'image/jpeg'}, 
+                    {maxExtent: new OpenLayers.Bounds(33861, 717605, 330846, 1019656), maxResolution: 296985/1024, projection:"EPSG:2805" } );
+        map.addLayer(layer);
+        
+        t.ok( !map.isValidLonLat(null), "null lonlat is not valid" );
+        t.ok( map.isValidLonLat(new OpenLayers.LonLat(33862, 717606)), "lonlat outside max extent is valid" );
+        t.ok( !map.isValidLonLat(new OpenLayers.LonLat(10, 10)), "lonlat outside max extent is not valid" );
+    }
+
+    function test_10_Map_getLayer(t) {
+        t.plan( 2 );    
+        map = new OpenLayers.Map($('map'));
+        layer = new OpenLayers.Layer.WMS('Test Layer', 
+                    "http://octo.metacarta.com/cgi-bin/mapserv", 
+                    {map: '/mapdata/vmap_wms.map', layers: 'basic', format: 'image/jpeg'}, 
+                    {maxExtent: new OpenLayers.Bounds(33861, 717605, 330846, 1019656), maxResolution: 296985/1024, projection:"EPSG:2805" } );
+
+        map.addLayer(layer);
+        var gotLayer = map.getLayer(layer.id);
+
+        t.ok( layer == gotLayer, "getLayer correctly returns layer" );
+
+        gotLayer = map.getLayer("chicken");
+
+        t.ok( gotLayer == null, "getLayer correctly returns null when layer not found");
+    }
+
+    function test_10_Map_setBaseLayer(t) {
+        t.plan( 4 );
+        
+        map = new OpenLayers.Map($('map'));
+
+        var wmslayer = new OpenLayers.Layer.WMS('Test Layer', 
+                    "http://octo.metacarta.com/cgi-bin/mapserv", 
+                    {map: '/mapdata/vmap_wms.map', layers: 'basic', format: 'image/jpeg'}, 
+                    {maxExtent: new OpenLayers.Bounds(33861, 717605, 330846, 1019656), maxResolution: 296985/1024, projection:"EPSG:2805" } );
+
+        var wmslayer2 = new OpenLayers.Layer.WMS('Test Layer2', 
+                    "http://octo.metacarta.com/cgi-bin/mapserv", 
+                    {map: '/mapdata/vmap_wms.map', layers: 'basic', format: 'image/jpeg'}, 
+                    {maxExtent: new OpenLayers.Bounds(33861, 717605, 330846, 1019656), maxResolution: 296985/1024, projection:"EPSG:2805" } );
+
+        map.addLayers([wmslayer, wmslayer2]);
+
+        t.ok(map.baseLayer == wmslayer, "default base layer is first one added");
+        
+        map.setBaseLayer(null);
+        t.ok(map.baseLayer == wmslayer, "setBaseLayer on null object does nothing (and does not break)");
+        
+        map.setBaseLayer("chicken");
+        t.ok(map.baseLayer == wmslayer, "setBaseLayer on non-layer object does nothing (and does not break)");
+        
+        map.setBaseLayer(wmslayer2);
+        t.ok(map.baseLayer == wmslayer2, "setbaselayer correctly sets 'baseLayer' property");
+
+    }
+
+    function test_12_Map_moveLayer (t) {
+        t.plan(10);
+        var ct  = 0;
+        map = new OpenLayers.Map($('map'));
+        var wmslayer = new OpenLayers.Layer.WMS('Test Layer', 
+                    "http://octo.metacarta.com/cgi-bin/mapserv", 
+                    {map: '/mapdata/vmap_wms.map', layers: 'basic', format: 'image/jpeg'}, 
+                    {maxExtent: new OpenLayers.Bounds(33861, 717605, 330846, 1019656), maxResolution: 296985/1024, projection:"EPSG:2805" } );
+
+        var wmslayer2 = new OpenLayers.Layer.WMS('Test Layer2', 
+                    "http://octo.metacarta.com/cgi-bin/mapserv", 
+                    {map: '/mapdata/vmap_wms.map', layers: 'basic', format: 'image/jpeg'}, 
+                    {maxExtent: new OpenLayers.Bounds(33861, 717605, 330846, 1019656), maxResolution: 296985/1024, projection:"EPSG:2805" } );
+
+        var wmslayer3 = new OpenLayers.Layer.WMS('Test Layer2', 
+                    "http://octo.metacarta.com/cgi-bin/mapserv", 
+                    {map: '/mapdata/vmap_wms.map', layers: 'basic', format: 'image/jpeg'}, 
+                    {maxExtent: new OpenLayers.Bounds(33861, 717605, 330846, 1019656), maxResolution: 296985/1024, projection:"EPSG:2805" } );
+
+        map.addLayers([wmslayer, wmslayer2, wmslayer3]);
+        map.events.register("changelayer", map, function (e) { ct++; });
+        t.eq( map.getNumLayers(), 3, "getNumLayers returns the number of layers" );
+        t.eq( map.getLayerIndex(wmslayer3), 2, "getLayerIndex returns the right index" );
+        map.raiseLayer(wmslayer3, 1);
+        t.eq( map.getLayerIndex(wmslayer3), 2, "can't moveLayer up past the top of the stack" );
+        map.raiseLayer(wmslayer, -1);
+        t.eq( map.getLayerIndex(wmslayer), 0, "can't moveLayer down past the bottom of the stack" );
+        map.raiseLayer(wmslayer3, -1);
+        t.eq( map.getLayerIndex(wmslayer3), 1, "can moveLayer down from the top" );
+        t.eq( parseInt(wmslayer3.div.style.zIndex), map.Z_INDEX_BASE['BaseLayer'] + 5,
+            "layer div has the right zIndex after moving down" );
+        map.raiseLayer(wmslayer, 2);
+        t.eq( map.getLayerIndex(wmslayer), 2, "can moveLayer up from the bottom" );
+        t.eq( parseInt(wmslayer.div.style.zIndex), map.Z_INDEX_BASE['BaseLayer'] + 2 * 5,
+            "layer div has the right zIndex after moving up" );
+        t.eq( map.getLayerIndex(wmslayer3), 0, "top layer is now on the bottom" );
+        t.eq( ct, 3, "raiseLayer triggered changelayer the right # of times" );
+    }
+    
+    function test_08_Map_setCenter(t) {
+        t.plan(1);
+        map = new OpenLayers.Map($('map'));
+        var baseLayer = new OpenLayers.Layer.WMS("Test Layer", 
+            "http://octo.metacarta.com/cgi-bin/mapserv?",
+            {map: "/mapdata/vmap_wms.map", layers: "basic"},
+            {maxResolution: 'auto', maxExtent: new OpenLayers.Bounds(-10,-10,10,10)});
+        map.addLayer(baseLayer);
+        var ll = new OpenLayers.LonLat(-100,-150);
+        map.setCenter(ll, 0);
+        t.ok(map.getCenter().equals(new OpenLayers.LonLat(0,0)), "safely sets out-of-bounds lonlat");
+    }
+
+    
+    function test_99_Map_destroy (t) {
+        t.plan( 2 );    
+        map = new OpenLayers.Map($('map'));
+        map.destroy();
+        t.eq( map.layers, null, "map.layers is null after destroy" );
+        t.eq( map.controls, null, "map.controls is null after destroy" );
+    }
+  // -->
+  </script>
+</head>
+<body>
+    <div id="map" style="width: 1080px; height: 600px;"/>
+</body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Map.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Marker.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Marker.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Marker.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,81 @@
+<html>
+<head>
+  <script src="../lib/OpenLayers.js"></script>
+  <script type="text/javascript"><!--
+    var marker; 
+    
+    function test_01_Marker_constructor (t) {
+        t.plan( 4 );
+        var ll = new OpenLayers.LonLat(2,1);
+        marker = new OpenLayers.Marker(ll,new OpenLayers.Icon());
+        t.ok( marker instanceof OpenLayers.Marker, "new OpenLayers.Marker returns Marker object" );
+        t.ok( marker.icon instanceof OpenLayers.Icon, "new marker.Icon returns Icon object" );
+        t.ok( marker.lonlat instanceof OpenLayers.LonLat, "new marker.lonlat returns LonLat object" );
+        t.ok( marker.lonlat.equals(ll), "marker.lonlat returns correct" );
+    }
+
+    function test_02_Marker_onScreen(t) {
+        t.plan( 2 );
+
+        var map = new OpenLayers.Map("map");
+
+        var url = "http://octo.metacarta.com/cgi-bin/mapserv";
+        layer = new OpenLayers.Layer.WMS(name, url);
+
+        map.addLayer(layer);
+        
+        mlayer = new OpenLayers.Layer.Markers('Test Layer');
+        map.addLayer(mlayer);
+               
+        map.zoomToExtent(new OpenLayers.Bounds(-50,-50,50,50));
+
+        //onscreen marker
+        var ll = new OpenLayers.LonLat(0,0);
+        var marker = new OpenLayers.Marker(ll);
+        mlayer.addMarker(marker);
+        
+        t.ok( marker.onScreen(), "marker knows it's onscreen" );
+
+        //offscreen marker
+        var ll = new OpenLayers.LonLat(100,100);
+        var marker2 = new OpenLayers.Marker(ll);
+        mlayer.addMarker(marker2);
+
+        t.ok( !marker2.onScreen(), "marker knows it's offscreen" );
+        
+    }
+
+    function test_02_Marker_setOpacity(t) {
+        t.plan( 2 );
+        
+        var map = new OpenLayers.Map("map");
+
+        var url = "http://octo.metacarta.com/cgi-bin/mapserv";
+        layer = new OpenLayers.Layer.WMS(name, url);
+
+        map.addLayer(layer);
+        
+        mlayer = new OpenLayers.Layer.Markers('Test Layer');
+        map.addLayer(mlayer);
+               
+        map.zoomToExtent(new OpenLayers.Bounds(-50,-50,50,50));
+
+        //onscreen marker
+        var ll = new OpenLayers.LonLat(0,0);
+        var marker = new OpenLayers.Marker(ll);
+        mlayer.addMarker(marker);
+
+        t.ok(!marker.icon.imageDiv.style.opacity, "default marker has no opacity");
+        
+        marker.setOpacity(0.5);
+
+        t.eq(marker.icon.imageDiv.style.opacity + "", "0.5", "marker.setOpacity() works");
+    }
+
+  // -->
+  </script>
+</head>
+<body>
+    <div id="map" style="width:500px;height:550px"></div>
+</body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Marker.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Pixel.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Pixel.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Pixel.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,96 @@
+<html>
+<head>
+  <script src="../lib/OpenLayers.js"></script>
+  <script type="text/javascript"><!--
+    var pixel; 
+    
+    function test_01_Pixel_constructor (t) {
+        t.plan( 4 );
+        pixel = new OpenLayers.Pixel(5,6);
+        t.ok( pixel instanceof OpenLayers.Pixel, "new OpenLayers.Pixel returns Pixel object" );
+        t.eq( pixel.CLASS_NAME, "OpenLayers.Pixel", "pixel.CLASS_NAME is set correctly");
+        t.eq( pixel.x, 5, "pixel.x is set correctly");
+        t.eq( pixel.y, 6, "pixel.y is set correctly");
+    }
+
+    function test_01a_Pixel_constructorFromString (t) {
+        t.plan( 4 );
+        pixel = new OpenLayers.Pixel("5","6");
+        t.ok( pixel instanceof OpenLayers.Pixel, "new OpenLayers.Pixel returns Pixel object" );
+        t.eq( pixel.CLASS_NAME, "OpenLayers.Pixel", "pixel.CLASS_NAME is set correctly");
+        t.eq( pixel.x, 5, "pixel.x is set correctly");
+        t.eq( pixel.y, 6, "pixel.y is set correctly");
+    }
+
+    function test_02_Pixel_toString(t) {
+        t.plan( 1 );
+        pixel = new OpenLayers.Pixel(5,6);
+        t.eq( pixel.toString(), "x=5,y=6", "pixel.toString() returns correctly");
+    }
+
+    function test_03_Pixel_clone(t) {
+        t.plan( 4 );
+        oldPixel = new OpenLayers.Pixel(5,6);
+        pixel = oldPixel.clone();
+        t.ok( pixel instanceof OpenLayers.Pixel, "clone returns new OpenLayers.Pixel object" );
+        t.eq( pixel.x, 5, "pixel.x is set correctly");
+        t.eq( pixel.y, 6, "pixel.y is set correctly");
+        
+        oldPixel.x = 100;
+        t.eq( pixel.x, 5, "changing oldPixel.x doesn't change pixel.x");
+    }
+
+    function test_06_Pixel_equals(t) {
+        t.plan( 5 );
+        pixel = new OpenLayers.Pixel(5,6);
+
+        px = new OpenLayers.Pixel(5,6);
+        t.eq( pixel.equals(px), true, "(5,6) equals (5,6)");
+
+        px = new OpenLayers.Pixel(1,6);
+        t.eq( pixel.equals(px), false, "(5,6) does not equal (1,6)");
+
+        px = new OpenLayers.Pixel(5,2);
+        t.eq( pixel.equals(px), false, "(5,6) does not equal (5,2)");
+
+        px = new OpenLayers.Pixel(1,2);
+        t.eq( pixel.equals(px), false, "(5,6) does not equal (1,2)");
+
+        t.ok( !pixel.equals(null), "equals() returns false on comparison to null");
+
+    }
+
+    function test_07_Pixel_add(t) {
+        t.plan( 4 );
+        oldPixel = new OpenLayers.Pixel(5,6);
+
+        pixel = oldPixel.add(10,20);
+
+        t.eq( oldPixel.x, 5, "oldPixel.x not modified by add operation");
+        t.eq( oldPixel.y, 6, "oldPixel.y not modified by add operation");
+
+        t.eq( pixel.x, 15, "pixel.x is set correctly");
+        t.eq( pixel.y, 26, "pixel.y is set correctly");
+    }
+
+    function test_08_Pixel_offset(t) {
+        t.plan( 4 );
+
+        var oldPixel = new OpenLayers.Pixel(5,6);
+        var offset = new OpenLayers.Pixel(10,20);
+
+        pixel = oldPixel.offset(offset);
+
+        t.eq( oldPixel.x, 5, "oldPixel.x not modified by offset operation");
+        t.eq( oldPixel.y, 6, "oldPixel.y not modified by offset operation");
+
+        t.eq( pixel.x, 15, "pixel.x is set correctly");
+        t.eq( pixel.y, 26, "pixel.y is set correctly");
+    }
+
+  // -->
+  </script>
+</head>
+<body>
+</body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Pixel.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Popup.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Popup.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Popup.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,115 @@
+<html>
+<head>
+  <script src="../lib/OpenLayers.js"></script>
+  <script type="text/javascript"><!--
+    var popup; 
+    
+    function test_01_Popup_default_constructor(t) {
+        t.plan( 8 );
+
+        var size = new OpenLayers.Size(OpenLayers.Popup.WIDTH,
+                                       OpenLayers.Popup.HEIGHT);
+        popup = new OpenLayers.Popup();
+
+        t.ok( popup instanceof OpenLayers.Popup, "new OpenLayers.Popup returns Popup object" );
+        t.ok(popup.id.startsWith("OpenLayers.Popup"), "valid default popupid");
+        var firstID = popup.id;
+        t.ok(popup.size.equals(size), "good default popup.size");
+        t.eq(popup.contentHTML, "", "good default popup.contentHTML");
+        t.eq(popup.backgroundColor, OpenLayers.Popup.COLOR, "good default popup.backgroundColor");
+        t.eq(popup.opacity, OpenLayers.Popup.OPACITY, "good default popup.opacity");
+        t.eq(popup.border, OpenLayers.Popup.BORDER, "good default popup.border");
+
+        
+        popup = new OpenLayers.Popup();
+        var newID = popup.id;
+        t.ok(newID != firstID, "default id generator creating unique ids");
+    }
+    
+    function test_02_Popup_constructor (t) {
+        t.plan( 5 );
+
+        var id = "chicken";
+        var w = 500;
+        var h = 400;
+        var sz = new OpenLayers.Size(w,h);
+        var lon = 5;
+        var lat = 40;
+        var ll = new OpenLayers.LonLat(lon, lat);
+        var content = "foo";
+
+        popup = new OpenLayers.Popup(id,
+                                     ll,
+                                     sz,
+                                     content);
+
+        t.ok( popup instanceof OpenLayers.Popup, "new OpenLayers.Popup returns Popup object" );
+        t.eq(popup.id, id, "popup.id set correctly");
+        t.ok(popup.lonlat.equals(ll), "popup.lonlat set correctly");
+        t.ok(popup.size.equals(sz), "popup.size set correctly");
+        t.eq(popup.contentHTML, content, "contentHTML porpoerty of set correctly");
+    }
+
+    function test_03_Popup_draw(t) {
+    
+        t.plan( 17 );
+        
+        var id = "chicken";
+        var x = 50;
+        var y = 100;
+        var w = 500;
+        var h = 400;
+        var content = "charlie";
+        var color = "red";
+        var opacity = 0.5;
+        var border = "1px solid";
+
+
+        popup = new OpenLayers.Popup(id);
+        popup.setSize(new OpenLayers.Size(w, h));
+        popup.setContentHTML(content);
+        popup.setBackgroundColor(color);
+        popup.setOpacity(opacity);
+        popup.setBorder(border);
+        popup.draw(new OpenLayers.Pixel(x, y));
+                                     
+        t.eq(popup.div.id, id, "popup.div.id set correctly");
+        t.eq(popup.div.style.left, x + "px", "left position of popup.div set correctly");
+        t.eq(popup.div.style.top, y + "px", "top position of popup.div set correctly");
+        t.eq(popup.div.style.width, w + "px", "width position of popup.div set correctly");
+        t.eq(popup.div.style.height, h + "px", "heightposition of popup.div set correctly");
+
+        var contentDiv = popup.div.childNodes[0];
+        
+        t.eq(contentDiv.className, "olPopupContent", "correct content div className");
+        t.eq(contentDiv.id, "chicken_contentDiv", "correct content div id");
+        t.eq(contentDiv.style.width, "200px", "correct content div width");	 
+        t.eq(contentDiv.style.height, "200px", "correct content div height");
+        t.eq(contentDiv.style.position, "relative", "correct content div position");
+        t.eq(contentDiv.style.overflow, "hidden", "correct content div overflow");
+        t.eq(contentDiv.innerHTML, content, "correct content div content");				 
+
+        t.eq(popup.div.style.backgroundColor, color, "good default popup.backgroundColor");
+
+        if (navigator.appName.indexOf("Microsoft") == -1) {
+            t.eq(parseFloat(popup.div.style.opacity), opacity, "good default popup.opacity");
+        } else {
+            t.eq(popup.div.style.filter, "alpha(opacity=" + opacity*100 + ")", "good default popup.opacity");
+        }
+        t.ok(popup.div.style.border.indexOf(border) != -1, "good default popup.border");
+
+        x += 50;
+        popup.moveTo(new OpenLayers.Pixel(x, y));
+        t.eq(popup.div.style.left, x + "px", "moveTo updates left position of popup.div correctly");
+        t.eq(popup.div.style.top, y + "px", "moveTo updates top position of popup.div correctly");
+        
+
+    }
+
+
+  // -->
+  </script>
+</head>
+<body>
+</body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Popup.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Size.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Size.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Size.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,67 @@
+<html>
+<head>
+  <script src="../lib/OpenLayers.js"></script>
+  <script type="text/javascript"><!--
+    var Size; 
+    
+    function test_01_Size_constructor (t) {
+        t.plan( 4 );
+        size = new OpenLayers.Size(5,6);
+        t.ok( size instanceof OpenLayers.Size, "new OpenLayers.Size returns size object" );
+        t.eq( size.CLASS_NAME, "OpenLayers.Size", "size.CLASS_NAME is set correctly");
+        t.eq( size.w, 5, "size.w is set correctly");
+        t.eq( size.h, 6, "size.h is set correctly");
+    }
+
+    function test_01a_Size_constructorFromString (t) {
+        t.plan( 4 );
+        size = new OpenLayers.Size("5","6");
+        t.ok( size instanceof OpenLayers.Size, "new OpenLayers.Size returns size object" );
+        t.eq( size.CLASS_NAME, "OpenLayers.Size", "size.CLASS_NAME is set correctly");
+        t.eq( size.w, 5, "size.w is set correctly");
+        t.eq( size.h, 6, "size.h is set correctly");
+    }
+    
+    function test_02_Size_toString(t) {
+        t.plan( 1 );
+        size = new OpenLayers.Size(5,6);
+        t.eq( size.toString(), "w=5,h=6", "size.toString() returns correctly");
+    }
+
+    function test_03_Size_clone(t) {
+        t.plan( 3 );
+
+        oldSize = new OpenLayers.Size(5,6);
+        size = oldSize.clone();
+        t.ok( size instanceof OpenLayers.Size, "clone returns new OpenLayers.Size object" );
+        t.ok( size.equals(oldSize), "new size is equal to old size correctly");
+        
+        oldSize.w = 100;
+        t.eq( size.w, 5, "changing oldSize.w doesn't change size.w");
+    }
+
+     function test_04_Size_equals(t) {
+        t.plan( 5 );
+        size = new OpenLayers.Size(5,6);
+
+        sz = new OpenLayers.Size(5,6);
+        t.eq( size.equals(sz), true, "(5,6) equals (5,6)");
+
+        sz = new OpenLayers.Size(1,6);
+        t.eq( size.equals(sz), false, "(5,6) does not equal (1,6)");
+
+        sz = new OpenLayers.Size(5,2);
+        t.eq( size.equals(sz), false, "(5,6) does not equal (5,2)");
+
+        sz = new OpenLayers.Size(1,2);
+        t.eq( size.equals(sz), false, "(5,6) does not equal (1,2)");
+
+        t.ok( !size.equals(null), "equals() returns false on comparison to null");
+    }
+
+  // -->
+  </script>
+</head>
+<body>
+</body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Size.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Tile.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Tile.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Tile.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,36 @@
+<html>
+<head>
+  <script src="../lib/OpenLayers.js"></script>
+  <script type="text/javascript"><!--
+    var tile; 
+    
+    function test_01_Tile_constructor (t) {
+        t.plan( 8 );
+        
+        var layer = new Object(); // bogus layer
+        var position = new OpenLayers.Pixel(10,20);
+        var bounds = new OpenLayers.Bounds(1,2,3,4);
+        var url = "bobob";
+        var size = new OpenLayers.Size(5,6);
+        
+        tile = new OpenLayers.Tile(layer, position, bounds, url, size);
+
+        t.ok( tile instanceof OpenLayers.Tile, "new OpenLayers.Tile returns Tile object" );
+        t.eq( tile.layer, layer, "tile.layer set correctly");
+        t.ok( tile.position.equals(position), "tile.position set correctly");
+        t.ok( tile.bounds.equals(bounds), "tile.bounds set correctly");
+        t.eq( tile.url, url, "tile.url set correctly");        
+        t.ok( tile.size.equals(size), "tile.size is set correctly" );
+
+        t.ok( tile.id != null, "tile is given an id");
+        t.ok( tile.id.startsWith("Tile_"), "tile's id starts correctly");
+    }
+
+
+  // -->
+  </script>
+</head>
+<body>
+</body>
+</html>
+


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Tile.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Tile_Image.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Tile_Image.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Tile_Image.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,133 @@
+<html>
+<head>
+  <script src="../lib/OpenLayers.js"></script>
+  <script type="text/javascript"><!--
+    var isMozilla = (navigator.userAgent.indexOf("compatible") == -1);
+    var tile; 
+    
+    function test_01_Tile_Image_constructor (t) {
+        t.plan( 6 );
+        
+        var layer = new OpenLayers.Layer.WMS( "OpenLayers WMS", 
+            "http://labs.metacarta.com/wms/vmap0?", {layers: 'basic'}); 
+
+        var position = new OpenLayers.Pixel(20,30);
+        var bounds = new OpenLayers.Bounds(1,2,3,4);
+        var url = "http://www.openlayers.org/dev/tests/tileimage";
+        var size = new OpenLayers.Size(5,6);
+        tile = new OpenLayers.Tile.Image(layer, position, bounds, url, size);
+        
+        t.ok( tile instanceof OpenLayers.Tile.Image, "new OpenLayers.Tile returns Tile object" );
+        t.ok( tile.layer == layer, "tile.layer is set correctly");
+        t.ok( tile.position.equals(position), "tile.position is set correctly");
+        t.ok( tile.bounds.equals(bounds), "tile.bounds is set correctly");
+        t.eq( tile.url, url, "tile.url is set correctly");
+        t.ok( tile.size.equals(size), "tile.size is set correctly");
+    }
+    function test_02_Tile_Image_draw (t) {
+        t.plan( 5 );
+
+        var map = new OpenLayers.Map('map');
+        
+        layer = new OpenLayers.Layer.WMS("Name", "http://labs.metacarta.com/TESTURL");  
+        map.addLayer(layer);  
+        var position = new OpenLayers.Pixel(20,30);
+        var bounds = new OpenLayers.Bounds(1,2,3,4);
+        var url = "http://www.openlayers.org/dev/tests/tileimage";
+        var size = new OpenLayers.Size(5,6);
+        tile = new OpenLayers.Tile.Image(layer, position, bounds, url, size);
+        
+        tile.draw();
+        var img = tile.imgDiv;
+        
+        if (!isMozilla)
+            t.ok( true, "skipping element test outside of Mozilla");
+        else
+            t.ok( tile.imgDiv instanceof HTMLElement, "tile.draw creates an image");
+        if (!isMozilla)
+            t.ok( true, "skipping element test outside of Mozilla");
+        else
+            t.ok( img instanceof HTMLImageElement, "tile.draw creates an image");
+        t.eq( img.src, "http://labs.metacarta.com/TESTURL?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&STYLES=&EXCEPTIONS=application/vnd.ogc.se_inimage&FORMAT=image/jpeg&SRS=EPSG:4326&BBOX=1,2,3,4&WIDTH=256&HEIGHT=256", "tile.draw creates an image");
+        t.eq( tile.imgDiv.style.width, "5px", "Image width is correct" );
+        t.eq( tile.imgDiv.style.height, "6px", "Image height is correct" );
+    }
+    function test_03_Tile_Image_OutsideMaxExtent(t) {
+        t.plan( 11 );
+        var position = new OpenLayers.Pixel(20,30);
+        var bounds = new OpenLayers.Bounds(1,2,3,4);
+        var url = "http://www.openlayers.org/dev/tests/tileimage";
+        var size = new OpenLayers.Size(5,6);
+        var map = new OpenLayers.Map('map');
+        var layer = new OpenLayers.Layer.WMS( "OpenLayers WMS", 
+            "http://labs.metacarta.com/wms/vmap0?", {layers: 'basic'}, {reproject:false}); 
+        map.addLayer(layer);
+        tile = new OpenLayers.Tile.Image(layer, position, new OpenLayers.Bounds(-185,-90,-180,90), url, size);
+        tile.draw()
+        t.eq(tile.imgDiv.src, "", "Images against side of maxextent don't load");
+        tile = new OpenLayers.Tile.Image(layer, position, new OpenLayers.Bounds(-181,-91,180,90), url, size);
+        tile.draw()
+        t.eq(tile.imgDiv.src, "http://labs.metacarta.com/wms/vmap0?LAYERS=basic&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&STYLES=&EXCEPTIONS=application/vnd.ogc.se_inimage&FORMAT=image/jpeg&SRS=EPSG:4326&BBOX=-181,-91,180,90&WIDTH=256&HEIGHT=256", "Images over edges of maxextent do load");
+        tile = new OpenLayers.Tile.Image(layer, position, new OpenLayers.Bounds(-181,-90,180,90), url, size);
+        tile.draw()
+        t.eq(tile.imgDiv.src, "http://labs.metacarta.com/wms/vmap0?LAYERS=basic&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&STYLES=&EXCEPTIONS=application/vnd.ogc.se_inimage&FORMAT=image/jpeg&SRS=EPSG:4326&BBOX=-181,-90,180,90&WIDTH=256&HEIGHT=256", "Images over edges of maxextent do load");
+        tile = new OpenLayers.Tile.Image(layer, position, new OpenLayers.Bounds(-180,-90,180,90), url, size);
+        tile.draw()
+        t.eq(tile.imgDiv.src, "http://labs.metacarta.com/wms/vmap0?LAYERS=basic&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&STYLES=&EXCEPTIONS=application/vnd.ogc.se_inimage&FORMAT=image/jpeg&SRS=EPSG:4326&BBOX=-180,-90,180,90&WIDTH=256&HEIGHT=256", "Image covering all of extent loads");
+        tile = new OpenLayers.Tile.Image(layer, position, new OpenLayers.Bounds(-80,-45,80,45), url, size);
+        tile.draw()
+        t.eq(tile.imgDiv.src, "http://labs.metacarta.com/wms/vmap0?LAYERS=basic&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&STYLES=&EXCEPTIONS=application/vnd.ogc.se_inimage&FORMAT=image/jpeg&SRS=EPSG:4326&BBOX=-80,-45,80,45&WIDTH=256&HEIGHT=256", "Image covering small part of extent loads");
+        tile = new OpenLayers.Tile.Image(layer, position, new OpenLayers.Bounds(-185,-95,185,95), url, size);
+        tile.draw()
+        t.eq(tile.imgDiv.src, "http://labs.metacarta.com/wms/vmap0?LAYERS=basic&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&STYLES=&EXCEPTIONS=application/vnd.ogc.se_inimage&FORMAT=image/jpeg&SRS=EPSG:4326&BBOX=-185,-95,185,95&WIDTH=256&HEIGHT=256", "Image covering more than all of extent loads");
+        
+        layer.displayOutsideMaxExtent=1;
+        tile = new OpenLayers.Tile.Image(layer, position, new OpenLayers.Bounds(-185,-90,-180,90), url, size);
+        tile.draw()
+        t.eq(tile.imgDiv.src, "http://labs.metacarta.com/wms/vmap0?LAYERS=basic&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&STYLES=&EXCEPTIONS=application/vnd.ogc.se_inimage&FORMAT=image/jpeg&SRS=EPSG:4326&BBOX=-185,-90,-180,90&WIDTH=256&HEIGHT=256", "Images against side of maxextent do load with displayOutsideMaxExtent");
+        tile = new OpenLayers.Tile.Image(layer, position, new OpenLayers.Bounds(-181,-90,180,90), url, size);
+        tile.draw()
+        t.eq(tile.imgDiv.src, "http://labs.metacarta.com/wms/vmap0?LAYERS=basic&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&STYLES=&EXCEPTIONS=application/vnd.ogc.se_inimage&FORMAT=image/jpeg&SRS=EPSG:4326&BBOX=-181,-90,180,90&WIDTH=256&HEIGHT=256", "Images over edges of maxextent do load with displayOutsideMaxExtent set");
+        tile = new OpenLayers.Tile.Image(layer, position, new OpenLayers.Bounds(-180,-90,180,90), url, size);
+        tile.draw()
+        t.eq(tile.imgDiv.src, "http://labs.metacarta.com/wms/vmap0?LAYERS=basic&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&STYLES=&EXCEPTIONS=application/vnd.ogc.se_inimage&FORMAT=image/jpeg&SRS=EPSG:4326&BBOX=-180,-90,180,90&WIDTH=256&HEIGHT=256", "Image covering all of extent loads with display outside max extent");
+        tile = new OpenLayers.Tile.Image(layer, position, new OpenLayers.Bounds(-80,-45,80,45), url, size);
+        tile.draw()
+        t.eq(tile.imgDiv.src, "http://labs.metacarta.com/wms/vmap0?LAYERS=basic&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&STYLES=&EXCEPTIONS=application/vnd.ogc.se_inimage&FORMAT=image/jpeg&SRS=EPSG:4326&BBOX=-80,-45,80,45&WIDTH=256&HEIGHT=256", "Image covering small part of extent loads with display outside max extent");
+        tile = new OpenLayers.Tile.Image(layer, position, new OpenLayers.Bounds(-185,-95,185,95), url, size);
+        tile.draw()
+        t.eq(tile.imgDiv.src, "http://labs.metacarta.com/wms/vmap0?LAYERS=basic&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&STYLES=&EXCEPTIONS=application/vnd.ogc.se_inimage&FORMAT=image/jpeg&SRS=EPSG:4326&BBOX=-185,-95,185,95&WIDTH=256&HEIGHT=256", "Image covering more than all of extent loads");
+    } 
+    function test_04_Tile_Image_Display_After_Move(t) {
+        t.plan(3);
+        var position = new OpenLayers.Pixel(20,30);
+        var bounds = new OpenLayers.Bounds(1,2,3,4);
+        var url = "http://www.openlayers.org/dev/tests/tileimage";
+        var size = new OpenLayers.Size(5,6);
+        var map = new OpenLayers.Map('map');
+        var layer = new OpenLayers.Layer.WMS( "OpenLayers WMS", 
+            "http://labs.metacarta.com/wms/vmap0?", {layers: 'basic'}); 
+        map.addLayer(layer);
+        map.zoomToMaxExtent();
+        tile = new OpenLayers.Tile.Image(layer, position, new OpenLayers.Bounds(-90,-85,-90,85), url, size);
+        tile.draw();
+        tile.moveTo(new OpenLayers.Bounds(-185,-90,-180,-80), new OpenLayers.Pixel(-180,-85), true);
+        t.delay_call( 1, function() { t.eq(tile.imgDiv.style.display, 'none', "Tile display is set to none.") } );
+        var layer = new OpenLayers.Layer.WMS( "OpenLayers WMS", 
+            "http://labs.metacarta.com/wms/vmap0?", {layers: 'basic'}, {'alpha':true});
+        map.addLayer(layer);
+        tile = new OpenLayers.Tile.Image(layer, position, new OpenLayers.Bounds(-90,-85,-90,85), url, size);
+        tile.draw();
+        tile.moveTo(new OpenLayers.Bounds(-185,-90,-180,-80), new OpenLayers.Pixel(-180,-85), true)
+        t.ok(tile.imgDiv.firstChild.src != tile.url, "Check to make sure that the alpha image URL really is different");
+        t.delay_call( 1, function() { t.eq(tile.imgDiv.style.display, 'none', "Alpha tile display is set to none.") } );
+        
+    }
+  // -->
+  </script>
+</head>
+<body>
+<div id="map" style="height:500px;width:500px"></div>
+</body>
+</html>
+


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Tile_Image.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Util.html
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Util.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Util.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,498 @@
+<html>
+<head>
+  <script src="../lib/OpenLayers.js"></script>
+  <script type="text/javascript"><!--
+    var isMozilla = (navigator.userAgent.indexOf("compatible") == -1);
+    var map; 
+    function test_01_Util_getImagesLocation (t) {
+        t.plan( 1 );
+        t.ok( OpenLayers.Util.getImagesLocation(), "../img/",
+                    "getImagesLocation()" );
+    }
+
+    function test_02_Util_Strings(t) {
+        t.plan(5);
+        
+        var str = "  chicken pox  ";
+
+        t.ok(str.contains("chicken"), "contains() function correctly finds an embedded string");
+        t.ok(!str.contains("marsupial"), "contains() function correctly does not finds an random string");
+        
+
+        var trimmedStr = str.trim();        
+
+        t.eq(trimmedStr, "chicken pox", "String.trim works correctly");
+
+        t.eq(trimmedStr.startsWith("chicken"), true, "String.startsWith correctly finds chicken");
+        t.eq(trimmedStr.startsWith("dolphin"), false, "String.startsWith correctly does not find turkey");
+
+
+    }
+
+    function test_03_Util_Array(t) {
+        t.plan( 2 );
+
+        var array = new Array(1,2,3,4,5);
+
+        OpenLayers.Util.removeItem(array, 3);
+        t.eq( array.toString(), "1,2,4,5", "Util.removeItem works");    
+
+        OpenLayers.Util.clearArray(array);
+        t.eq( array.toString(), "", "Util.clearArray works");    
+        
+    }
+
+    function test_04_Util_createDiv(t) {
+        t.plan( 24 );
+
+        var id = "boo";
+        var px = new OpenLayers.Pixel(5,5);
+        var sz = new OpenLayers.Size(10,10);
+        var img = "http://www.openlayers.org/images/OpenLayers.trac.png";
+        var position = "absolute";
+        var border = "13px solid";
+        var overflow = "hidden";
+        var opacity = 0.5;
+
+        var div = OpenLayers.Util.createDiv(id, px, sz, img, position, border, overflow, opacity);
+
+        if (!isMozilla)
+            t.ok( true, "skipping element test outside of Mozilla");
+        else
+            t.ok( div instanceof HTMLDivElement, "createDiv creates a valid HTMLDivElement" );
+        t.eq( div.id, id, "div.id set correctly");    
+        t.eq( div.style.left, px.x + "px", "div.style.left set correctly");    
+        t.eq( div.style.top, px.y + "px", "div.style.top set correctly");    
+
+        t.eq( div.style.width, sz.w + "px", "div.style.width set correctly");    
+        t.eq( div.style.height, sz.h + "px", "div.style.height set correctly");    
+
+        t.eq( div.style.backgroundImage, "url(" + img + ")", "div.style.backgroundImage correctly");    
+
+        t.eq( div.style.position, position, "div.style.positionset correctly");    
+        t.ok( (div.style.border.indexOf(border) != -1), "div.style.border set correctly");    
+        t.eq( div.style.overflow, overflow, "div.style.overflow set correctly");    
+        t.eq( div.style.opacity + "", opacity + "", "elemnt.style.opacity set correctly");    
+        var filterString = 'alpha(opacity=' + (opacity * 100) + ')';
+        t.eq( div.style.filter, filterString, "element.style.filter set correctly");
+
+        //test defaults
+        var div = OpenLayers.Util.createDiv();
+
+        if (!isMozilla)
+            t.ok( true, "skipping element test outside of Mozilla");
+        else
+            t.ok( div instanceof HTMLDivElement, "createDiv creates a valid HTMLDivElement" );
+        t.ok( (div.id != ""), "div.id set correctly");    
+        t.eq(div.style.left, "", "div.style.left set correctly");    
+        t.eq(div.style.top, "", "div.style.top set correctly");    
+
+        t.eq( div.style.width, "", "div.style.width set correctly");    
+        t.eq( div.style.height, "", "div.style.height set correctly");    
+
+        t.eq(div.style.backgroundImage, "", "div.style.backgroundImage correctly");    
+
+        t.eq( div.style.position, "absolute", "div.style.positionset correctly");    
+        t.eq( div.style.border, "", "div.style.border set correctly");    
+        t.eq(div.style.overflow, "", "div.style.overflow set correctly");    
+        t.ok( !div.style.opacity, "elemnt.style.opacity set correctly");    
+        t.ok( !div.style.filter, "element.style.filter set correctly");
+
+    }
+
+    function test_05_Util_createImage(t) {
+        t.plan( 22 );
+
+        var img = "http://www.openlayers.org/images/OpenLayers.trac.png";
+        var sz = new OpenLayers.Size(10,10);
+        var xy = new OpenLayers.Pixel(5,5);
+        var position = "absolute";
+        var id = "boo";
+        var border = "1px solid";
+        var opacity = 0.5;
+
+        var image = OpenLayers.Util.createImage(id, xy, sz, img, position, border, opacity);
+
+        if (!isMozilla)
+            t.ok( true, "skipping element test outside of Mozilla");
+        else
+            t.ok( image instanceof HTMLImageElement, "createImage creates a valid HTMLImageElement" );
+        t.eq( image.id, id, "image.id set correctly");    
+        t.eq( image.style.left, xy.x + "px", "image.style.left set correctly");    
+        t.eq( image.style.top, xy.y + "px", "image.style.top set correctly");    
+
+        t.eq( image.style.width, sz.w + "px", "image.style.width set correctly");    
+        t.eq( image.style.height, sz.h + "px", "image.style.height set correctly");    
+
+        t.ok( (image.style.border.indexOf(border) != -1), "image.style.border set correctly");    
+        t.eq( image.src, img, "image.style.backgroundImage correctly");    
+        t.eq( image.style.position, position, "image.style.position set correctly");    
+        t.eq( image.style.opacity+"", opacity + "", "image.style.opacity set correctly");    
+        var filterString = 'alpha(opacity=' + (opacity * 100) + ')';
+        t.eq( image.style.filter, filterString, "element.style.filter set correctly");
+
+        //test defaults
+        var image = OpenLayers.Util.createImage();
+
+        if (!isMozilla)
+            t.ok( true, "skipping element test outside of Mozilla");
+        else
+            t.ok( image instanceof HTMLImageElement, "createDiv creates a valid HTMLDivElement" );
+        t.ok( (image.id != ""), "image.id set to something");    
+        t.eq( image.style.left, "", "image.style.left set correctly");    
+        t.eq( image.style.top, "", "image.style.top set correctly");    
+
+        t.eq( image.style.width, "", "image.style.width set correctly");    
+        t.eq( image.style.height, "", "image.style.height set correctly");    
+
+        t.ok((image.style.border == ""), "image.style.border set correctly");    
+        t.eq(image.src, "", "image.style.backgroundImage correctly");    
+        t.eq( image.style.position, "relative", "image.style.positionset correctly");    
+        t.ok( !image.style.opacity, "elemnt.style.opacity default unset");    
+        t.ok( !image.style.filter, "element.style.filter default unset");
+
+    }
+
+    function test_06_Util_applyDefaults(t) {
+    
+        t.plan(4);
+        
+        var to = { a: "abra",
+                   b: "blorg"
+                 };
+
+        var from = { b: "zoink",
+                     c: "press"
+                   };
+
+        OpenLayers.Util.applyDefaults(to, from);
+
+        t.ok( to instanceof Object, " applyDefaults returns an object");
+        t.eq( to["a"], "abra", "key present in to but not from maintained");
+        t.eq( to["b"], "blorg", "key present in to and from, maintained in to");
+        t.eq( to["c"], "press", "key present in from and not to successfully copied to to");
+    }
+
+    function test_07_Util_getParameterString(t) {
+        t.plan( 1 );
+
+        var params = { foo: "bar",
+                       chicken: 1.5
+                     }
+
+        t.eq( OpenLayers.Util.getParameterString(params), "foo=bar&chicken=1.5", "getParameterString returns correctly");    
+    }
+
+    function test_08_Util_createAlphaImageDiv(t) {
+        t.plan( 19 );
+
+        var img = "http://www.openlayers.org/images/OpenLayers.trac.png";
+        var sz = new OpenLayers.Size(10,10);
+        var xy = new OpenLayers.Pixel(5,5);
+        var position = "absolute";
+        var id = "boo";
+        var border = "1px solid";
+        var sizing = "crop";
+        var opacity = 0.5;
+
+        var imageDiv = OpenLayers.Util.createAlphaImageDiv(id, xy, sz, img, position, border, sizing, opacity);
+
+        if (!isMozilla)
+            t.ok( true, "skipping element test outside of Mozilla");
+        else
+            t.ok( imageDiv instanceof HTMLDivElement, "createDiv creates a valid HTMLDivElement" );
+
+        t.eq( imageDiv.id, id, "image.id set correctly");    
+        t.eq( imageDiv.style.left, xy.x + "px", "image.style.left set correctly");    
+        t.eq( imageDiv.style.top, xy.y + "px", "image.style.top set correctly");    
+
+        t.eq( imageDiv.style.width, sz.w + "px", "image.style.width set correctly");    
+        t.eq( imageDiv.style.height, sz.h + "px", "image.style.height set correctly");    
+
+        t.eq( imageDiv.style.position, position, "image.style.positionset correctly");    
+        t.eq( imageDiv.style.opacity+"", opacity + "", "elemnt.style.opacity set correctly");    
+        
+        var filterString;
+        if (OpenLayers.Util.alphaHack()) {
+            filterString = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='http://www.openlayers.org/images/OpenLayers.trac.png', sizingMethod='crop') alpha(opacity=50)";
+        } else {
+            filterString = 'alpha(opacity=' + (opacity * 100) + ')';
+        }        
+        t.eq( imageDiv.style.filter, filterString, "element.style.filter set correctly");
+
+
+        image = imageDiv.firstChild;
+
+        if (!isMozilla)
+            t.ok( true, "skipping element test outside of Mozilla");
+        else
+            t.ok( image instanceof HTMLImageElement, "createImage creates a valid HTMLImageElement" );
+        t.eq( image.id, id + "_innerImage", "image.id set correctly");    
+
+        t.eq( image.style.width, sz.w + "px", "image.style.width set correctly");    
+        t.eq( image.style.height, sz.h + "px", "image.style.height set correctly");    
+
+        t.ok( (image.style.border.indexOf(border) != -1), "image.style.border set correctly");    
+        t.eq( image.style.position, "relative", "image.style.positionset correctly");    
+
+        if (OpenLayers.Util.alphaHack()) {
+        
+            t.eq(imageDiv.style.display, "inline-block", "imageDiv.style.display set correctly");
+
+            var filter = "progid:DXImageTransform.Microsoft" +
+                         ".AlphaImageLoader(src='" + img + "', " +
+                         "sizingMethod='" + sizing + "') alpha(opacity=50)";
+            t.eq(imageDiv.style.filter, filter, "div filter value correctly set");
+
+            filter = "progid:DXImageTransform.Microsoft.Alpha(opacity=0)";
+            t.eq(image.style.filter, filter, "image filter set correctly");
+
+        } else {
+            t.eq( image.src, img, "image.style.backgroundImage correctly");    
+            t.ok(true, "div filter value not set (not in IE)");
+            t.ok(true, "image filter value not set (not in IE)");
+        }
+
+        var imageDiv = OpenLayers.Util.createAlphaImageDiv(id, xy, sz, img, position, border);
+        if (OpenLayers.Util.alphaHack()) {
+            var filter = "progid:DXImageTransform.Microsoft" +
+                         ".AlphaImageLoader(src='" + img + "', " +
+                         "sizingMethod='scale')";
+            t.eq(imageDiv.style.filter, filter, "sizingMethod default correctly set to scale");
+        } else {
+            t.ok(true);
+        }        
+
+    }
+
+    function test_09_Util_modifyDOMElement(t) {
+
+        t.plan( 10 );
+
+        var id = "boo";
+        var px = new OpenLayers.Pixel(5,5);
+        var sz = new OpenLayers.Size(10,10);
+        var position = "absolute";
+        var border = "1px solid";
+        var overflow = "hidden";
+        var opacity = 1/2;
+
+        var element = document.createElement("div");
+
+        OpenLayers.Util.modifyDOMElement(element, id, px, sz, position, 
+                                         border, overflow, opacity);
+
+        t.eq( element.id, id, "element.id set correctly");    
+        t.eq( element.style.left, px.x + "px", "element.style.left set correctly");    
+        t.eq( element.style.top, px.y + "px", "element.style.top set correctly");    
+
+        t.eq( element.style.width, sz.w + "px", "element.style.width set correctly");    
+        t.eq( element.style.height, sz.h + "px", "element.style.height set correctly");    
+
+        t.eq( element.style.position, position, "element.style.position set correctly");    
+        t.ok( (element.style.border.indexOf(border) != -1), "element.style.border set correctly");    
+        t.eq( element.style.overflow, overflow, "element.style.overflow set correctly");    
+        t.eq( element.style.opacity+"", opacity + "", "elemnt.style.opacity set correctly");    
+        var filterString = 'alpha(opacity=' + (opacity * 100) + ')';
+        t.eq( element.style.filter, filterString, "element.style.filter set correctly");
+    }
+
+    function test_09_Util_modifyAlphaImageDiv(t) {
+        t.plan( 19 );
+
+        var imageDiv = OpenLayers.Util.createAlphaImageDiv();
+
+        var img = "http://www.openlayers.org/images/OpenLayers.trac.png";
+        var sz = new OpenLayers.Size(10,10);
+        var xy = new OpenLayers.Pixel(5,5);
+        var position = "absolute";
+        var id = "boo";
+        var border = "1px solid";
+        var sizing = "crop";
+        var opacity = 0.5;
+
+        OpenLayers.Util.modifyAlphaImageDiv(imageDiv, id, xy, sz, img, position, border, sizing, opacity);
+
+        if (OpenLayers.Util.alphaHack())
+            t.ok( true, "skipping element test outside of Mozilla");
+        else
+            t.ok( imageDiv instanceof HTMLDivElement, "createDiv creates a valid HTMLDivElement" );
+
+        t.eq( imageDiv.id, id, "image.id set correctly");    
+        t.eq( imageDiv.style.left, xy.x + "px", "image.style.left set correctly");    
+        t.eq( imageDiv.style.top, xy.y + "px", "image.style.top set correctly");    
+
+        t.eq( imageDiv.style.width, sz.w + "px", "image.style.width set correctly");    
+        t.eq( imageDiv.style.height, sz.h + "px", "image.style.height set correctly");    
+
+        t.eq( imageDiv.style.position, position, "image.style.position set correctly");    
+        t.eq( imageDiv.style.opacity+"", opacity + "", "elemnt.style.opacity set correctly");    
+
+        
+
+        image = imageDiv.firstChild;
+
+        var filterString;
+        if (OpenLayers.Util.alphaHack()) {
+            filterString = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='http://www.openlayers.org/images/OpenLayers.trac.png', sizingMethod='crop') alpha(opacity=50)";
+            t.ok( true, "skipping element test outside of Mozilla");
+        } else {
+            var filterString = 'alpha(opacity=' + (opacity * 100) + ')';
+            t.ok( image instanceof HTMLImageElement, "createImage creates a valid HTMLImageElement" );
+        }
+        t.eq( imageDiv.style.filter, filterString, "element.style.filter set correctly");
+        t.eq( image.id, id + "_innerImage", "image.id set correctly");    
+
+        t.eq( image.style.width, sz.w + "px", "image.style.width set correctly");    
+        t.eq( image.style.height, sz.h + "px", "image.style.height set correctly");    
+
+        t.ok( (image.style.border.indexOf(border) != -1), "image.style.border set correctly");    
+        t.eq( image.style.position, "relative", "image.style.positionset correctly");    
+
+        if (OpenLayers.Util.alphaHack()) {
+        
+            t.eq(imageDiv.style.display, "inline-block", "imageDiv.style.display set correctly");
+
+            var filter = "progid:DXImageTransform.Microsoft" +
+                         ".AlphaImageLoader(src='" + img + "', " +
+                         "sizingMethod='" + sizing + "') alpha(opacity=" + opacity *100 + ")";
+            t.eq(imageDiv.style.filter, filter, "div filter value correctly set");
+
+            filter = "progid:DXImageTransform.Microsoft.Alpha(opacity=0)";
+            t.eq(image.style.filter, filter, "image filter set correctly");
+
+        } else {
+            t.eq( image.src, img, "image.style.backgroundImage correctly");    
+            t.ok(true, "div filter value not set (not in IE)");
+            t.ok(true, "image filter value not set (not in IE)");
+        }
+
+        var imageDiv = OpenLayers.Util.createAlphaImageDiv(id, xy, sz, img, position, border, "scale", opacity);
+        if (OpenLayers.Util.alphaHack()) {
+            var filter = "progid:DXImageTransform.Microsoft" +
+                         ".AlphaImageLoader(src='" + img + "', " +
+                         "sizingMethod='scale') alpha(opacity=" + opacity *100 + ")";
+            t.eq(imageDiv.style.filter, filter, "sizingMethod default correctly set to scale");
+        } else {
+            t.ok(true);
+        }        
+   
+    }
+       
+    function test_10_Util_upperCaseObject(t) {
+    
+        t.plan(8);
+        
+        var aKey = "chicken";
+        var aValue = "pot pie";
+
+        var bKey = "blorg";
+        var bValue = "us maximus";
+        
+        var obj = new Object();
+        obj[aKey] = aValue;        
+        obj[bKey] = bValue;        
+             
+        var uObj = OpenLayers.Util.upperCaseObject(obj);          
+
+        //make sure old object not modified
+        t.eq(obj[aKey], aValue, "old lowercase value still present in old obj");
+        t.eq(obj[bKey], bValue, "old lowercase value still present in old obj");
+
+        t.eq(obj[aKey.toUpperCase()], null, "new uppercase value not present in old obj");
+        t.eq(obj[bKey.toUpperCase()], null, "new uppercase value not present in old obj");
+
+        //make sure new object modified
+        t.eq(uObj[aKey], null, "old lowercase value not present");
+        t.eq(uObj[bKey], null, "old lowercase value not present");
+
+        t.eq(uObj[aKey.toUpperCase()], aValue, "new uppercase value present");
+        t.eq(uObj[bKey.toUpperCase()], bValue, "new uppercase value present");
+    }
+    
+    function test_11_Util_createUniqueID(t) {
+        t.plan(2);
+        
+        var id = OpenLayers.Util.createUniqueID();
+        t.ok( id.startsWith("id_"), "default OpenLayers.Util.createUniqueID starts id correctly");
+
+        var id = OpenLayers.Util.createUniqueID("chicken");
+        t.ok( id.startsWith("chicken"), "OpenLayers.Util.createUniqueID starts id correctly");
+    }
+    
+    function test_12_Util_limitSigDigs(t) {
+
+        t.plan(7);
+
+        var x;
+
+        x = 123456;
+        t.eq(x.limitSigDigs(3), 123000, "correctly rounds down");
+
+        x = 555555;
+        t.eq(x.limitSigDigs(3), 556000, "correctly rounds up");
+
+        x = 66;
+        t.eq(x.limitSigDigs(3), 66, "correctly handles number smaller than sigdig");
+
+        t.eq(x.limitSigDigs(null), 0, "correctly handles null sigdig");
+        t.eq(x.limitSigDigs(0), 0, "correctly handles 0 sigdig");
+        t.eq(x.limitSigDigs(-1), 0, "correctly handles negative sigdig");
+
+        x = 0;
+        t.eq(x.limitSigDigs(2), 0, "correctly handles 0 number");
+
+
+
+    }
+    
+    function test_13_Util_normalizeScale(t) {
+        t.plan(2); 
+        
+        //normal scale
+        var scale = 1/5;
+        t.eq( OpenLayers.Util.normalizeScale(scale), scale, "normalizing a normal scale does nothing");
+
+        //funky scale
+        var scale = 5;
+        t.eq( OpenLayers.Util.normalizeScale(scale), 1/5, "normalizing a wrong scale works!");
+
+
+    }
+    
+    function test_13_Util_getScaleResolutionTranslation(t) {
+        t.plan(4); 
+        
+        var scale = 1/150000000; 
+        var resolution = OpenLayers.Util.getResolutionFromScale(scale);
+        t.eq(resolution.toFixed(6), "0.476217", "Calculated correct resolution for " + scale);
+
+        var scale = 1/150000000; 
+        var resolution = OpenLayers.Util.getResolutionFromScale(scale, 'm');
+        t.eq(resolution.toFixed(6), "52916.638092", "Calculated correct resolution for " + scale);
+
+        scale = 150000000; 
+        resolution = OpenLayers.Util.getResolutionFromScale(scale);
+        t.eq(resolution.toFixed(6), "0.476217", "Calculated correct resolution for " + scale);
+
+        scale = 150000000; 
+        resolution = OpenLayers.Util.getResolutionFromScale(scale);
+        t.eq(OpenLayers.Util.getScaleFromResolution(resolution), scale, "scale->resolution->scale works");
+    }
+    function test_14_Util_getImgLocation(t) {
+        t.plan(3);
+        OpenLayers.ImgPath = "foo/";
+        t.eq(OpenLayers.Util.getImagesLocation(), "foo/", "ImgPath works as expected."); 
+        OpenLayers.ImgPath = null;
+        t.eq(OpenLayers.Util.getImagesLocation().substr(OpenLayers.Util.getImagesLocation().length-4,4), "img/", "ImgPath works as expected when not set."); 
+
+        OpenLayers.ImgPath = '';
+        t.eq(OpenLayers.Util.getImagesLocation().substr(OpenLayers.Util.getImagesLocation().length-4,4), "img/", "ImgPath works as expected when set to ''."); 
+    }
+  // -->
+  </script>
+</head>
+<body>
+    <div id="map" style="width: 1024px; height: 512px;"/>
+</body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/test_Util.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/throws.js
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/throws.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/throws.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,83 @@
+/*
+
+  throws.js -- Adds a `throws_` method to AnotherWay test objects.
+
+  Copyright 2005 MetaCarta, Inc., released under the BSD License.
+
+
+  A reference to this file needs to be added to `run-tests.html` in the
+  head element after the AnotherWay classes are created:
+
+    <script type="text/javascript" src="throws.js"></script>
+
+  Then, it can be used just like the `ok`, `fail` and other such methods
+  in your unit tests.
+
+  e.g. 
+
+   t.throws_(function () {new OpenLayers.View.Map.Dynamic();},
+             ReferenceError("No container supplied."),
+             "OpenLayers.View.Map.Dynamic instantiation with no container "
+             + "must throw.");
+
+  This was inspired by the `assertRaises` method of Python's unittest
+  library.
+
+  Possible future enhancements:
+
+    * Contribute to official AnotherWay distribution.
+    * Use `apply` rather than require a inner function (or as an option).
+    * Preserve the stack fields.
+
+ */
+
+Test.AnotherWay._test_object_t.prototype.throws_ = 
+function (fn, expectedException, doc) {
+    /*
+      
+       Executes the supplied function object catching any exception(s)
+       thrown, then verifies the supplied expected exception occurred.
+      
+       If no exception is thrown the test fails.
+
+       If an exception is thrown and it does not match the supplied
+       expected exception the test fails.
+
+       If the exception thrown matches the supplied expected exception
+       the test passes.
+
+       Two exceptions "match" if Test.AnotherWay's `eq` method considers
+       the two equal when their respective stacks are ignored.
+
+                      fn - The function object to be executed
+       expectedException - The exception object expected to result
+                     doc - Description of the test
+
+       Note: The name of this method is `throws_` (with a trailing
+             underscore) as `throws` is a reserved identifier and can
+             not be used as a method name.
+
+       Note: This function does not preserve the stack field associated
+             with either exception.
+
+     */
+    var theCaughtException = null;
+
+    try {
+        fn();
+    } catch (innerCaughtException) {
+        // As `innerCaughtException` is not visible outside the scope
+        // of this `catch` block we need to make it visible explicitly. 
+        theCaughtException = innerCaughtException;
+    }
+
+    if (theCaughtException) {
+        // We delete the stacks before comparison as they will never match.
+        delete theCaughtException.stack;
+        delete expectedException.stack;
+        this.eq(theCaughtException, expectedException, doc);
+    } else {
+        this.fail(doc);
+    }
+}
+


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tests/throws.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/theme/default/style.css
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/theme/default/style.css	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/theme/default/style.css	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,79 @@
+div.olMapViewport {
+   -moz-user-select: none 
+}
+
+.olLayerGoogleCopyright {
+    left: 2px;
+    bottom: 2px;  
+}
+.olLayerGooglePoweredBy {
+    left: 2px;
+    bottom: 15px;   
+}
+
+.olControlScale {
+    right: 3px;
+    bottom: 3em;
+    display: block;
+    position: absolute;
+}
+.olControlPermalink {
+    right: 3px;
+    bottom: 1.5em;
+    display: block;
+    position: absolute;
+} 
+
+div.olControlMousePosition {
+    bottom: 0em;
+    right: 3px;
+    display: block;
+    position: absolute;
+    font-family: Arial;
+    font-size: smaller;
+}
+
+.olControlOverviewMapContainer {
+    position: absolute;
+    bottom: 0px;
+    right: 0px;
+}
+
+.olControlOverviewMapElement {
+    padding: 10px 18px 10px 10px;
+    background-color: #00008B;
+    -moz-border-radius: 1em 0 0 0;
+}
+
+.olControlOverviewMapMinimizeButton {
+    right: 0px;
+    bottom: 80px;
+}    
+
+.olControlOverviewMapMaximizeButton {
+    right: 0px;
+    bottom: 80px;
+}
+
+.olControlOverviewMapExtentRectangle {
+    border: 2px dotted red;
+}
+.olLayerGeoRSSDescription {
+    float:left;
+    width:100%;
+    overflow:auto;
+    font-size:1.0em;
+}
+.olLayerGeoRSSClose {
+    float:right;
+    color:gray;
+    font-size:1.2em;
+    font-family:sans-serif;
+}
+.olLayerGeoRSSTitle {
+    float:left;font-size:1.2em;
+}
+
+.olPopupContent {
+    padding:5px;
+}    


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/theme/default/style.css
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tools/README.txt
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/tools/README.txt	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/tools/README.txt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,14 @@
+This directory contains tools used in the packaging or deployment of OpenLayers.
+
+Javascript minimizing tools:
+
+ * jsmin.c, jsmin.py:
+   jsmin.py is a direct translation of the jsmin.c code into Python. jsmin.py
+   will therefore run anyplace Python runs... but at significantly slower speed.
+ 
+ * shrinksafe.py
+   shrinksafe.py calls out to a third party javascript shrinking service. This 
+   creates file sizes about 4% smaller (as of commit 501) of the OpenLayers 
+   code. However, this also has the side effect of making you dependant on the 
+   web service -- and since that service sometimes goes dead, it's risky to 
+   depend on it.


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tools/README.txt
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tools/jsmin.c
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/tools/jsmin.c	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/tools/jsmin.c	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,272 @@
+/* jsmin.c
+   2006-05-04
+
+Copyright (c) 2002 Douglas Crockford  (www.crockford.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+The Software shall be used for Good, not Evil.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+
+static int   theA;
+static int   theB;
+static int   theLookahead = EOF;
+
+
+/* isAlphanum -- return true if the character is a letter, digit, underscore,
+        dollar sign, or non-ASCII character.
+*/
+
+static int
+isAlphanum(int c)
+{
+    return ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') ||
+        (c >= 'A' && c <= 'Z') || c == '_' || c == '$' || c == '\\' ||
+        c > 126);
+}
+
+
+/* get -- return the next character from stdin. Watch out for lookahead. If
+        the character is a control character, translate it to a space or
+        linefeed.
+*/
+
+static int
+get()
+{
+    int c = theLookahead;
+    theLookahead = EOF;
+    if (c == EOF) {
+        c = getc(stdin);
+    }
+    if (c >= ' ' || c == '\n' || c == EOF) {
+        return c;
+    }
+    if (c == '\r') {
+        return '\n';
+    }
+    return ' ';
+}
+
+
+/* peek -- get the next character without getting it.
+*/
+
+static int
+peek()
+{
+    theLookahead = get();
+    return theLookahead;
+}
+
+
+/* next -- get the next character, excluding comments. peek() is used to see
+        if a '/' is followed by a '/' or '*'.
+*/
+
+static int
+next()
+{
+    int c = get();
+    if  (c == '/') {
+        switch (peek()) {
+        case '/':
+            for (;;) {
+                c = get();
+                if (c <= '\n') {
+                    return c;
+                }
+            }
+        case '*':
+            get();
+            for (;;) {
+                switch (get()) {
+                case '*':
+                    if (peek() == '/') {
+                        get();
+                        return ' ';
+                    }
+                    break;
+                case EOF:
+                    fprintf(stderr, "Error: JSMIN Unterminated comment.\n");
+                    exit(1);
+                }
+            }
+        default:
+            return c;
+        }
+    }
+    return c;
+}
+
+
+/* action -- do something! What you do is determined by the argument:
+        1   Output A. Copy B to A. Get the next B.
+        2   Copy B to A. Get the next B. (Delete A).
+        3   Get the next B. (Delete B).
+   action treats a string as a single character. Wow!
+   action recognizes a regular expression if it is preceded by ( or , or =.
+*/
+
+static void
+action(int d)
+{
+    switch (d) {
+    case 1:
+        putc(theA, stdout);
+    case 2:
+        theA = theB;
+        if (theA == '\'' || theA == '"') {
+            for (;;) {
+                putc(theA, stdout);
+                theA = get();
+                if (theA == theB) {
+                    break;
+                }
+                if (theA <= '\n') {
+                    fprintf(stderr,
+"Error: JSMIN unterminated string literal: %c\n", theA);
+                    exit(1);
+                }
+                if (theA == '\\') {
+                    putc(theA, stdout);
+                    theA = get();
+                }
+            }
+        }
+    case 3:
+        theB = next();
+        if (theB == '/' && (theA == '(' || theA == ',' || theA == '=' ||
+                theA == ':' || theA == '[' || theA == '!' || theA == '&' || 
+                theA == '|')) {
+            putc(theA, stdout);
+            putc(theB, stdout);
+            for (;;) {
+                theA = get();
+                if (theA == '/') {
+                    break;
+                } else if (theA =='\\') {
+                    putc(theA, stdout);
+                    theA = get();
+                } else if (theA <= '\n') {
+                    fprintf(stderr,
+"Error: JSMIN unterminated Regular Expression literal.\n", theA);
+                    exit(1);
+                }
+                putc(theA, stdout);
+            }
+            theB = next();
+        }
+    }
+}
+
+
+/* jsmin -- Copy the input to the output, deleting the characters which are
+        insignificant to JavaScript. Comments will be removed. Tabs will be
+        replaced with spaces. Carriage returns will be replaced with linefeeds.
+        Most spaces and linefeeds will be removed.
+*/
+
+static void
+jsmin()
+{
+    theA = '\n';
+    action(3);
+    while (theA != EOF) {
+        switch (theA) {
+        case ' ':
+            if (isAlphanum(theB)) {
+                action(1);
+            } else {
+                action(2);
+            }
+            break;
+        case '\n':
+            switch (theB) {
+            case '{':
+            case '[':
+            case '(':
+            case '+':
+            case '-':
+                action(1);
+                break;
+            case ' ':
+                action(3);
+                break;
+            default:
+                if (isAlphanum(theB)) {
+                    action(1);
+                } else {
+                    action(2);
+                }
+            }
+            break;
+        default:
+            switch (theB) {
+            case ' ':
+                if (isAlphanum(theA)) {
+                    action(1);
+                    break;
+                }
+                action(3);
+                break;
+            case '\n':
+                switch (theA) {
+                case '}':
+                case ']':
+                case ')':
+                case '+':
+                case '-':
+                case '"':
+                case '\'':
+                    action(1);
+                    break;
+                default:
+                    if (isAlphanum(theA)) {
+                        action(1);
+                    } else {
+                        action(3);
+                    }
+                }
+                break;
+            default:
+                action(1);
+                break;
+            }
+        }
+    }
+}
+
+
+/* main -- Output any command line arguments as comments
+        and then minify the input.
+*/
+extern int
+main(int argc, char* argv[])
+{
+    int i;
+    for (i = 1; i < argc; i += 1) {
+        fprintf(stdout, "// %s\n", argv[i]);
+    }
+    jsmin();
+    return 0;
+}


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tools/jsmin.c
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tools/jsmin.py
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/tools/jsmin.py	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/tools/jsmin.py	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,212 @@
+#!/usr/bin/python
+
+# This code is original from jsmin by Douglas Crockford, it was translated to
+# Python by Baruch Even. The original code had the following copyright and
+# license.
+#
+# /* jsmin.c
+#    2003-04-21
+# 
+# Copyright (c) 2002 Douglas Crockford  (www.crockford.com)
+# 
+# Permission is hereby granted, free of charge, to any person obtaining a copy of
+# this software and associated documentation files (the "Software"), to deal in
+# the Software without restriction, including without limitation the rights to
+# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+# of the Software, and to permit persons to whom the Software is furnished to do
+# so, subject to the following conditions:
+# 
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+# 
+# The Software shall be used for Good, not Evil.
+# 
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+# */
+
+from StringIO import StringIO
+
+def jsmin(js):
+	ins = StringIO(js)
+	outs = StringIO()
+	JavascriptMinify().minify(ins, outs)
+	str = outs.getvalue()
+	if len(str) > 0 and str[0] == '\n':
+		str = str[1:]
+	return str
+
+def isAlphanum(c):
+	"""return true if the character is a letter, digit, underscore,
+           dollar sign, or non-ASCII character.
+	"""
+	return ((c >= 'a' and c <= 'z') or (c >= '0' and c <= '9') or
+	        (c >= 'A' and c <= 'Z') or c == '_' or c == '$' or c == '\\' or (c is not None and ord(c) > 126));
+
+class UnterminatedComment(Exception):
+	pass
+
+class UnterminatedStringLiteral(Exception):
+	pass
+
+class UnterminatedRegularExpression(Exception):
+	pass
+
+class JavascriptMinify(object):
+
+	def _outA(self):
+		self.outstream.write(self.theA)
+	def _outB(self):
+		self.outstream.write(self.theB)
+
+	def _get(self):
+		"""return the next character from stdin. Watch out for lookahead. If
+		   the character is a control character, translate it to a space or
+		   linefeed.
+		"""
+		c = self.theLookahead
+		self.theLookahead = None
+		if c == None:
+			c = self.instream.read(1)
+		if c >= ' ' or c == '\n':
+			return c
+		if c == '': # EOF
+			return '\000'
+		if c == '\r':
+			return '\n'
+		return ' '
+
+	def _peek(self):
+		self.theLookahead = self._get()
+		return self.theLookahead
+
+	def _next(self):
+		"""get the next character, excluding comments. peek() is used to see
+		   if a '/' is followed by a '/' or '*'.
+		"""
+		c = self._get()
+		if c == '/':
+			p = self._peek()
+			if p == '/':
+				c = self._get()
+				while c > '\n':
+					c = self._get()
+				return c
+			if p == '*':
+				c = self._get()
+				while 1:
+					c = self._get()
+					if c == '*':
+						if self._peek() == '/':
+							self._get()
+							return ' '
+					if c == '\000':
+						raise UnterminatedComment()
+
+		return c
+
+	def _action(self, action):
+		"""do something! What you do is determined by the argument:
+		   1   Output A. Copy B to A. Get the next B.
+		   2   Copy B to A. Get the next B. (Delete A).
+		   3   Get the next B. (Delete B).
+		   action treats a string as a single character. Wow!
+		   action recognizes a regular expression if it is preceded by ( or , or =.
+		"""
+		if action <= 1:
+			self._outA()
+			
+		if action <= 2:
+			self.theA = self.theB
+			if self.theA == "'" or self.theA == '"':
+				while 1:
+					self._outA()
+					self.theA = self._get()
+					if self.theA == self.theB:
+						break
+					if self.theA <= '\n':
+						raise UnterminatedStringLiteral()
+					if self.theA == '\\':
+						self._outA()
+						self.theA = self._get()
+
+
+		if action <= 3:
+			self.theB = self._next()
+			if self.theB == '/' and (self.theA == '(' or self.theA == ',' or self.theA == '='):
+				self._outA()
+				self._outB()
+				while 1:
+					self.theA = self._get()
+					if self.theA == '/':
+						break
+					elif self.theA == '\\':
+						self._outA()
+						self.theA = self._get()
+					elif self.theA <= '\n':
+						raise UnterminatedRegularExpression()
+					self._outA()
+				self.theB = self._next()
+
+
+	def _jsmin(self):
+		"""Copy the input to the output, deleting the characters which are
+		   insignificant to JavaScript. Comments will be removed. Tabs will be
+		   replaced with spaces. Carriage returns will be replaced with linefeeds.
+		   Most spaces and linefeeds will be removed.
+		"""
+		self.theA = '\n'
+		self._action(3)
+
+		while self.theA != '\000':
+			if self.theA == ' ':
+				if isAlphanum(self.theB):
+					self._action(1)
+				else:
+					self._action(2)
+			elif self.theA == '\n':
+				if self.theB in ['{', '[', '(', '+', '-']:
+					self._action(1)
+				elif self.theB == ' ':
+					self._action(3)
+				else:
+					if isAlphanum(self.theB):
+						self._action(1)
+					else:
+						self._action(2)
+			else:
+				if self.theB == ' ':
+					if isAlphanum(self.theA):
+						self._action(1)
+					else:
+						self._action(3)
+				elif self.theB == '\n':
+					if self.theA in ['}', ']', ')', '+', '-', '"', '\'']:
+						self._action(1)
+					else:
+						if isAlphanum(self.theA):
+							self._action(1)
+						else:
+							self._action(3)
+				else:
+					self._action(1)
+
+	def minify(self, instream, outstream):
+		self.instream = instream
+		self.outstream = outstream
+		self.theA = None
+		self.thaB = None
+		self.theLookahead = None
+
+		self._jsmin()
+		self.instream.close()
+
+if __name__ == '__main__':
+	import sys
+	jsm = JavascriptMinify()
+	jsm.minify(sys.stdin, sys.stdout)


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tools/jsmin.py
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tools/mergejs.py
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/tools/mergejs.py	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/tools/mergejs.py	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,232 @@
+#!/usr/bin/env python
+#
+# Merge multiple JavaScript source code files into one.
+#
+# Usage:
+# This script requires source files to have dependencies specified in them.
+#
+# Dependencies are specified with a comment of the form:
+#
+#     // @requires <file path>
+#
+#  e.g.
+#
+#    // @requires Geo/DataSource.js
+#
+#  or (ideally) within a class comment definition
+#
+#     /**
+#      * @class
+#      *
+#      * @requires OpenLayers/Layer.js
+#      */
+#
+# This script should be executed like so:
+#
+#     mergejs.py <output.js> <directory> [...]
+#
+# e.g.
+#
+#     mergejs.py openlayers.js Geo/ CrossBrowser/
+#
+#  This example will cause the script to walk the `Geo` and
+#  `CrossBrowser` directories--and subdirectories thereof--and import
+#  all `*.js` files encountered. The dependency declarations will be extracted
+#  and then the source code from imported files will be output to 
+#  a file named `openlayers.js` in an order which fulfils the dependencies
+#  specified.
+#
+#
+# Note: This is a very rough initial version of this code.
+#
+# -- Copyright 2005-2006 MetaCarta, Inc. / OpenLayers project --
+#
+
+# TODO: Allow files to be excluded. e.g. `Crossbrowser/DebugMode.js`?
+# TODO: Report error when dependency can not be found rather than KeyError.
+
+import re
+import os
+import sys
+
+SUFFIX_JAVASCRIPT = ".js"
+
+RE_REQUIRE = "@requires (.*)\n" # TODO: Ensure in comment?
+class SourceFile:
+    """
+    Represents a Javascript source code file.
+    """
+
+    def __init__(self, filepath, source):
+        """
+        """
+        self.filepath = filepath
+        self.source = source
+
+        self.requiredBy = []
+
+
+    def _getRequirements(self):
+        """
+        Extracts the dependencies specified in the source code and returns
+        a list of them.
+        """
+        # TODO: Cache?
+        return re.findall(RE_REQUIRE, self.source)
+
+    requires = property(fget=_getRequirements, doc="")
+
+
+
+def usage(filename):
+    """
+    Displays a usage message.
+    """
+    print "%s [-c <config file>] <output.js> <directory> [...]" % filename
+
+
+class Config:
+    """
+    Represents a parsed configuration file.
+
+    A configuration file should be of the following form:
+
+        [first]
+        3rd/prototype.js
+        core/application.js
+        core/params.js
+
+        [last]
+        core/api.js
+
+        [exclude]
+        3rd/logger.js
+
+    All headings are required.
+
+    The files listed in the `first` section will be forced to load
+    *before* all other files (in the order listed). The files in `last`
+    section will be forced to load *after* all the other files (in the
+    order listed).
+
+    The files list in the `exclude` section will not be imported.
+    
+    """
+
+    def __init__(self, filename):
+        """
+        Parses the content of the named file and stores the values.
+        """
+        lines = [line[:-1] # Assumes end-of-line character is present
+                 for line in open(filename)
+                 if line != "\n"] # Skip blank lines
+
+        self.forceFirst = lines[lines.index("[first]") + 1:lines.index("[last]")]
+
+        self.forceLast = lines[lines.index("[last]") + 1:lines.index("[include]")]
+        self.include =  lines[lines.index("[include]") + 1:lines.index("[exclude]")]
+        self.exclude =  lines[lines.index("[exclude]") + 1:]
+
+if __name__ == "__main__":
+    import getopt
+
+    options, args = getopt.getopt(sys.argv[1:], "-c:")
+    
+    try:
+        outputFilename = args[0]
+    except IndexError:
+        usage(sys.argv[0])
+        raise SystemExit
+    else:
+        sourceDirectory = args[1]
+        if not sourceDirectory:
+            usage(sys.argv[0])
+            raise SystemExit
+
+    cfg = None
+    if options and options[0][0] == "-c":
+        filename = options[0][1]
+        print "Parsing configuration file: %s" % filename
+
+        cfg = Config(filename)
+
+    print cfg.include
+    allFiles = []
+
+    ## Find all the Javascript source files
+    for root, dirs, files in os.walk(sourceDirectory):
+	for filename in files:
+	    if filename.endswith(SUFFIX_JAVASCRIPT) and not filename.startswith("."):
+		filepath = os.path.join(root, filename)[len(sourceDirectory)+1:]
+                if cfg and cfg.include:
+                    if filepath in cfg.include or filepath in cfg.forceFirst:
+                        allFiles.append(filepath)
+                elif (not cfg) or (filepath not in cfg.exclude):
+		    allFiles.append(filepath)
+
+    ## Header inserted at the start of each file in the output
+    HEADER = "/* " + "=" * 70 + "    %s\n" + "   " + "=" * 70 + " */\n\n"
+
+    files = {}
+
+    order = [] # List of filepaths to output, in a dependency satisfying order 
+
+    ## Import file source code
+    ## TODO: Do import when we walk the directories above?
+    for filepath in allFiles:
+        print "Importing: %s" % filepath
+	fullpath = os.path.join(sourceDirectory, filepath)
+        content = open(fullpath, "U").read() # TODO: Ensure end of line @ EOF?
+        files[filepath] = SourceFile(filepath, content) # TODO: Chop path?
+
+    ## Resolve the dependencies
+    print "\nResolving dependencies...\n"
+
+    from toposort import toposort
+
+    nodes = []
+    routes = []
+
+    for filepath, info in files.items():
+        nodes.append(filepath)
+        for neededFilePath in info.requires:
+            routes.append((neededFilePath, filepath))
+
+    for dependencyLevel in toposort(nodes, routes):
+        for filepath in dependencyLevel:
+            order.append(filepath)
+
+
+    ## Move forced first and last files to the required position
+    if cfg:
+        print "Re-ordering files...\n"
+        order = cfg.forceFirst + [item
+                     for item in order
+                     if ((item not in cfg.forceFirst) and
+                         (item not in cfg.forceLast))] + cfg.forceLast
+
+    ## Double check all dependencies have been met
+    for fp in order:
+        if max([order.index(rfp) for rfp in files[fp].requires] +
+               [order.index(fp)]) != order.index(fp):
+            print "Inconsistent!"
+            raise SystemExit
+
+
+    ## Output the files in the determined order
+    result = []
+
+    for fp in order:
+        f = files[fp]
+        print "Exporting: ", f.filepath
+        result.append(HEADER % f.filepath)
+        source = f.source
+        result.append(source)
+        if not source.endswith("\n"):
+            result.append("\n")
+
+    print "\nTotal files merged: %d " % len(allFiles)
+
+    print "\nGenerating: %s" % (outputFilename)
+
+    open(outputFilename, "w").write("".join(result))


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tools/mergejs.py
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tools/release.sh
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/tools/release.sh	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/tools/release.sh	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,15 @@
+#!/bin/sh
+VERSION=$1
+echo "Building OpenLayers $VERSION"
+svn export http://svn.openlayers.org/tags/openlayers/release-$VERSION OpenLayers-$VERSION
+cd OpenLayers-$VERSION/build
+./build.sh
+mkdir /www/openlayers/htdocs/api/$VERSION
+cp OpenLayers.js /www/openlayers/htdocs/api/$VERSION
+cd ..
+cp -a img/ /www/openlayers/htdocs/api/$VERSION
+cp -a theme/ /www/openlayers/htdocs/api/$VERSION
+rm tools/*.pyc
+cd ..
+tar -zvcf OpenLayers-$VERSION.tar.gz OpenLayers-$VERSION
+cp OpenLayers-$VERSION.tar.gz /www/openlayers/htdocs/download/


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tools/release.sh
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tools/shrinksafe.py
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/tools/shrinksafe.py	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/tools/shrinksafe.py	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,54 @@
+#!/usr/bin/env python
+#
+# Script to provide a wrapper around the ShrinkSafe "web service"
+# <http://alex.dojotoolkit.org/shrinksafe/>
+#
+
+#
+# We use this script for two reasons:
+#
+#  * This avoids having to install and configure Java and the standalone
+#    ShrinkSafe utility.
+#
+#  * The current ShrinkSafe standalone utility was broken when we last
+#    used it.
+#
+
+import sys
+
+import urllib
+import urllib2
+
+URL_SHRINK_SAFE = "http://alex.dojotoolkit.org/shrinksafe/shrinksafe.php"
+
+# This would normally be dynamically generated:
+BOUNDARY_MARKER = "---------------------------72288400411964641492083565382"
+                   
+if __name__ == "__main__":
+    ## Grab the source code
+    try:
+        sourceFilename = sys.argv[1]
+    except:
+        print "Usage: %s (<source filename>|-)" % sys.argv[0]
+        raise SystemExit
+
+    if sourceFilename == "-":
+        sourceCode = sys.stdin.read()
+        sourceFilename = "stdin.js"
+    else:
+        sourceCode = open(sourceFilename).read()
+        
+    ## Create the request replicating posting of the form from the web page
+    request = urllib2.Request(url=URL_SHRINK_SAFE)
+    request.add_header("Content-Type",
+                       "multipart/form-data; boundary=%s" % BOUNDARY_MARKER)
+    request.add_data("""
+--%s
+Content-Disposition: form-data; name="shrinkfile[]"; filename="%s"
+Content-Type: application/x-javascript
+
+%s
+""" % (BOUNDARY_MARKER, sourceFilename, sourceCode))
+
+    ## Deliver the result
+    print urllib2.urlopen(request).read(),


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tools/shrinksafe.py
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tools/toposort.py
===================================================================
--- trunk/PhotoOp/root/static/js/OpenLayers-2.2/tools/toposort.py	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/OpenLayers-2.2/tools/toposort.py	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,260 @@
+#
+# According to <http://www.vrplumber.com/programming/> this file
+# is licensed under a BSD-style license. We only use the section
+# originally by Tim Peters.
+#
+# TODO: The use of this code needs to be okayed by someone.
+#
+
+class RecursionError( OverflowError, ValueError ):
+    '''Unable to calculate result because of recursive structure'''
+    
+
+def sort(nodes, routes, noRecursion=1):
+    '''Passed a list of node IDs and a list of source,dest ID routes
+    attempt to create a list of stages where each sub list
+    is one stage in a process.
+    '''
+    children, parents = _buildChildrenLists(routes)
+    # first stage is those nodes
+    # having no incoming routes...
+    stage = []
+    stages = [stage]
+    taken = []
+    for node in nodes:
+        if (not parents.get(node)):
+            stage.append (node)
+    if nodes and not stage:
+        # there is no element which does not depend on
+        # some other element!!!
+        stage.append( nodes[0])
+    taken.extend( stage )
+    nodes = filter ( lambda x, l=stage: x not in l, nodes )
+    while nodes:
+        previousStageChildren = []
+        nodelen = len(nodes)
+        # second stage are those nodes
+        # which are direct children of the first stage
+        for node in stage:
+            for child in children.get (node, []):
+                if child not in previousStageChildren and child not in taken:
+                    previousStageChildren.append(child)
+                elif child in taken and noRecursion:
+                    raise RecursionError( (child, node) )
+        # unless they are children of other direct children...
+        # TODO, actually do that...
+        stage = previousStageChildren
+        removes = []
+        for current in stage:
+            currentParents = parents.get( current, [] )
+            for parent in currentParents:
+                if parent in stage and parent != current:
+                    # might wind up removing current...
+                    if not current in parents.get(parent, []):
+                        # is not mutually dependent...
+                        removes.append( current )
+        for remove in removes:
+            while remove in stage:
+                stage.remove( remove )
+        stages.append( stage)
+        taken.extend( stage )
+        nodes = filter ( lambda x, l=stage: x not in l, nodes )
+        if nodelen == len(nodes):
+            if noRecursion:
+                raise RecursionError( nodes )
+            else:
+                stages.append( nodes[:] )
+                nodes = []
+    return stages
+
+def _buildChildrenLists (routes):
+    childrenTable = {}
+    parentTable = {}
+    for sourceID,destinationID in routes:
+        currentChildren = childrenTable.get( sourceID, [])
+        currentParents = parentTable.get( destinationID, [])
+        if not destinationID in currentChildren:
+            currentChildren.append ( destinationID)
+        if not sourceID in currentParents:
+            currentParents.append ( sourceID)
+        childrenTable[sourceID] = currentChildren
+        parentTable[destinationID] = currentParents
+    return childrenTable, parentTable
+
+
+def toposort (nodes, routes, noRecursion=1):
+    '''Topological sort from Tim Peters, fairly efficient
+    in comparison (it seems).'''
+    #first calculate the recursion depth
+    
+    dependencies = {}
+    inversedependencies = {}
+    if not nodes:
+        return []
+    if not routes:
+        return [nodes]
+    for node in nodes:
+        dependencies[ node ] = (0, node)
+        inversedependencies[ node ] = []
+    
+    
+    for depended, depends in routes:
+        # is it a null rule
+        try:
+            newdependencylevel, object = dependencies.get ( depends, (0, depends))
+        except TypeError:
+            print depends
+            raise
+        dependencies[ depends ] = (newdependencylevel + 1,  depends)
+        # "dependency (existence) of depended-on"
+        newdependencylevel,object = dependencies.get ( depended, (0, depended) )
+        dependencies[ depended ] = (newdependencylevel, depended)
+        # Inverse dependency set up
+        dependencieslist = inversedependencies.get ( depended, [])
+        dependencieslist.append (depends)
+        inversedependencies[depended] = dependencieslist
+    ### Now we do the actual sorting
+    # The first task is to create the sortable
+    # list of dependency-levels
+    sortinglist = dependencies.values()
+    sortinglist.sort ()
+    output = []
+    while sortinglist:
+        deletelist = []
+        generation = []
+        output.append( generation)
+        while sortinglist and sortinglist[0][0] == 0:
+            number, object = sortinglist[0]
+            generation.append ( object )
+            deletelist.append( object )
+            for inverse in inversedependencies.get(object, () ):
+                try:
+                    oldcount, inverse = dependencies [ inverse]
+                    if oldcount > 0:
+                        # will be dealt with on later pass
+                        dependencies [ inverse] = (oldcount-1, inverse)
+                    else:
+                        # will be dealt with on this pass,
+                        # so needs not to be in the sorting list next time
+                        deletelist.append( inverse )
+                    # just in case a loop comes through
+                    inversedependencies[object] = []
+                except KeyError:
+                    # dealing with a recursion-breaking run...
+                    pass
+            del sortinglist [0]
+        # if no elements could be deleted, then
+        # there is something which depends upon itself
+        if not deletelist:
+            if noRecursion:
+                raise RecursionError( sortinglist )
+            else:
+                # hack so that something gets deleted...
+##                import pdb
+##                pdb.set_trace()
+                dependencies[sortinglist[0][1]] = (0,sortinglist[0][1])
+        # delete the items that were dealt with
+        for item in deletelist:
+            try:
+                del dependencies [ item ]
+            except KeyError:
+                pass
+        # need to recreate the sortinglist
+        sortinglist = dependencies.values()
+        if not generation:
+            output.remove( generation )
+        sortinglist.sort ()
+    return output
+
+
+
+
+
+if __name__ == "__main__":
+
+    nodes = ['a', 'b', 'c', 'd', 'e', 'f']
+    route = [('a', 'b'), ('b', 'c'), ('b', 'd'), ('e','f')]
+
+    for x in  toposort( nodes, route):
+        for a in x:
+            print a
+
+    raise SystemExit
+
+
+
+    import pprint, traceback
+    nodes= [ 0,1,2,3,4,5 ]
+    testingValues = [
+        [ (0,1),(1,2),(2,3),(3,4),(4,5)],
+        [ (0,1),(0,2),(1,2),(3,4),(4,5)],
+        [
+        (0,1),
+        (0,2),
+        (0,2),
+                    (2,4),
+                    (2,5),
+                (3,2),
+        (0,3)],
+        [
+        (0,1), # 3-element cycle test, no orphan nodes
+        (1,2),
+        (2,0),
+                    (2,4),
+                    (2,5),
+                (3,2),
+        (0,3)],
+        [
+        (0,1),
+        (1,1),
+        (1,1),
+                (1,4),
+                (1,5),
+                (1,2),
+        (3,1),
+        (2,1),
+        (2,0)],
+        [
+            (0,1),
+            (1,0),
+            (0,2),
+            (0,3),
+        ],
+        [
+            (0,1),
+            (1,0),
+            (0,2),
+            (3,1),
+        ],
+    ]
+    print 'sort, no recursion allowed'
+    for index in range(len(testingValues)):
+##        print '    %s -- %s'%( index, testingValues[index])
+        try:
+            print '        ', sort( nodes, testingValues[index] )
+        except:
+            print 'exception raised'
+    print 'toposort, no recursion allowed'
+    for index in range(len(testingValues)):
+##        print '    %s -- %s'%( index, testingValues[index])
+        try:
+            print '        ', toposort( nodes, testingValues[index] )
+        except:
+            print 'exception raised'
+    print 'sort, recursion allowed'
+    for index in range(len(testingValues)):
+##        print '    %s -- %s'%( index, testingValues[index])
+        try:
+            print '        ', sort( nodes, testingValues[index],0 )
+        except:
+            print 'exception raised'
+    print 'toposort, recursion allowed'
+    for index in range(len(testingValues)):
+##        print '    %s -- %s'%( index, testingValues[index])
+        try:
+            print '        ', toposort( nodes, testingValues[index],0 )
+        except:
+            print 'exception raised'
+        
+        
+    


Property changes on: trunk/PhotoOp/root/static/js/OpenLayers-2.2/tools/toposort.py
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/AdapterRegistry.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/AdapterRegistry.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/AdapterRegistry.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,72 @@
+/*
+	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.AdapterRegistry");
+dojo.require("dojo.lang.func");
+
+dojo.AdapterRegistry = function(){
+    /***
+        A registry to facilitate adaptation.
+
+        Pairs is an array of [name, check, wrap] triples
+        
+        All check/wrap functions in this registry should be of the same arity.
+    ***/
+    this.pairs = [];
+}
+
+dojo.lang.extend(dojo.AdapterRegistry, {
+    register: function (name, check, wrap, /* optional */ override){
+        /***
+			The check function should return true if the given arguments are
+			appropriate for the wrap function.
+
+			If override is given and true, the check function will be given
+			highest priority.  Otherwise, it will be the lowest priority
+			adapter.
+        ***/
+
+        if (override) {
+            this.pairs.unshift([name, check, wrap]);
+        } else {
+            this.pairs.push([name, check, wrap]);
+        }
+    },
+
+    match: function (/* ... */) {
+        /***
+			Find an adapter for the given arguments.
+
+			If no suitable adapter is found, throws NotFound.
+        ***/
+        for(var i = 0; i < this.pairs.length; i++){
+            var pair = this.pairs[i];
+            if(pair[1].apply(this, arguments)){
+                return pair[2].apply(this, arguments);
+            }
+        }
+		throw new Error("No match found");
+        // dojo.raise("No match found");
+    },
+
+    unregister: function (name) {
+        /***
+			Remove a named adapter from the registry
+        ***/
+        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;
+    }
+});


Property changes on: trunk/PhotoOp/root/static/js/src/AdapterRegistry.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/Deferred.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/Deferred.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/Deferred.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,309 @@
+/*
+	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.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 <http://twistedmatrix.com>.
+
+	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;
+	},
+
+	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);
+		}
+	}
+});


Property changes on: trunk/PhotoOp/root/static/js/src/Deferred.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/animation/Animation.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/animation/Animation.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/animation/Animation.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,217 @@
+/*
+	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.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.Line*/ curve, /*int*/ duration, /*Decimal?*/ accel, /*int?*/ repeatCount, /*int?*/ rate) {
+	// public properties
+	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(gotoStart) {
+		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() {
+		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() {
+		if( !this._active || this._paused ) {
+			this.play();
+		} else {
+			this.pause();
+		}
+	},
+
+	gotoPercent: function(pct, andPlay) {
+		clearTimeout(this._timer);
+		this._active = true;
+		this._paused = true;
+		this._percent = pct;
+		if( andPlay ) { this.play(); }
+	},
+
+	stop: function(gotoEnd) {
+		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() {
+		if( this._active ) {
+			return this._paused ? "paused" : "playing";
+		} else {
+			return "stopped";
+		}
+	},
+
+	// "private" methods
+	_cycle: function() {
+		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();
+					}
+				}
+			}
+		}
+	}
+});


Property changes on: trunk/PhotoOp/root/static/js/src/animation/Animation.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/animation/AnimationEvent.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/animation/AnimationEvent.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/animation/AnimationEvent.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,40 @@
+/*
+	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.animation.AnimationEvent");
+
+dojo.require("dojo.lang");
+
+dojo.animation.AnimationEvent = function(anim, type, coords, sTime, cTime, eTime, dur, pct, fps) {
+	this.type = type; // "animate", "begin", "end", "play", "pause", "stop"
+	this.animation = anim;
+
+	this.coords = coords;
+	this.x = coords[0];
+	this.y = coords[1];
+	this.z = coords[2];
+
+	this.startTime = sTime;
+	this.currentTime = cTime;
+	this.endTime = eTime;
+
+	this.duration = dur;
+	this.percent = pct;
+	this.fps = fps;
+};
+dojo.lang.extend(dojo.animation.AnimationEvent, {
+	coordsAsInts: function() {
+		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;
+	}
+});


Property changes on: trunk/PhotoOp/root/static/js/src/animation/AnimationEvent.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/animation/AnimationSequence.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/animation/AnimationSequence.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/animation/AnimationSequence.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,136 @@
+/*
+	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.animation.AnimationSequence");
+dojo.require("dojo.animation.AnimationEvent");
+dojo.require("dojo.animation.Animation");
+
+dojo.animation.AnimationSequence = function(repeatCount){
+	this._anims = [];
+	this.repeatCount = repeatCount || 0;
+}
+
+dojo.lang.extend(dojo.animation.AnimationSequence, {
+	repeateCount: 0,
+
+	_anims: [],
+	_currAnim: -1,
+
+	onBegin: null,
+	onEnd: null,
+	onNext: null,
+	handler: null,
+
+	add: function() {
+		for(var i = 0; i < arguments.length; i++) {
+			this._anims.push(arguments[i]);
+			arguments[i]._animSequence = this;
+		}
+	},
+
+	remove: function(anim) {
+		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() {
+		for(var i = 0; i < this._anims.length; i++) {
+			this._anims[i]._animSequence = null;
+		}
+		this._anims = [];
+		this._currAnim = -1;
+	},
+
+	clear: function() {
+		this.removeAll();
+	},
+
+	play: function(gotoStart) {
+		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() {
+		if( this._anims[this._currAnim] ) {
+			this._anims[this._currAnim].pause();
+		}
+	},
+
+	playPause: function() {
+		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() {
+		if( this._anims[this._currAnim] ) {
+			this._anims[this._currAnim].stop();
+		}
+	},
+
+	status: function() {
+		if( this._anims[this._currAnim] ) {
+			return this._anims[this._currAnim].status();
+		} else {
+			return "stopped";
+		}
+	},
+
+	_setCurrent: function(anim) {
+		for(var i = 0; i < this._anims.length; i++) {
+			if( this._anims[i] == anim ) {
+				this._currAnim = i;
+				break;
+			}
+		}
+	},
+
+	_playNext: function() {
+		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;
+			}
+		}
+	}
+});


Property changes on: trunk/PhotoOp/root/static/js/src/animation/AnimationSequence.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/animation/Timer.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/animation/Timer.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/animation/Timer.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,39 @@
+/*
+	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.animation.Timer");
+dojo.require("dojo.lang.func");
+
+dojo.animation.Timer = function(intvl){
+	var timer = null;
+	this.isRunning = false;
+	this.interval = intvl;
+
+	this.onTick = function(){};
+	this.onStart = null;
+	this.onStop = null;
+
+	this.setInterval = function(ms){
+		if (this.isRunning) window.clearInterval(timer);
+		this.interval = ms;
+		if (this.isRunning) timer = window.setInterval(dojo.lang.hitch(this, "onTick"), this.interval);
+	};
+
+	this.start = function(){
+		if (typeof this.onStart == "function") this.onStart();
+		this.isRunning = true;
+		timer = window.setInterval(this.onTick, this.interval);
+	};
+	this.stop = function(){
+		if (typeof this.onStop == "function") this.onStop();
+		this.isRunning = false;
+		window.clearInterval(timer);
+	};
+};


Property changes on: trunk/PhotoOp/root/static/js/src/animation/Timer.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/animation/__package__.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/animation/__package__.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/animation/__package__.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,18 @@
+/*
+	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.kwCompoundRequire({
+	common: [
+		"dojo.animation.AnimationEvent",
+		"dojo.animation.Animation",
+		"dojo.animation.AnimationSequence"
+	]
+});
+dojo.provide("dojo.animation.*");


Property changes on: trunk/PhotoOp/root/static/js/src/animation/__package__.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/animation.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/animation.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/animation.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -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.provide("dojo.animation");
+dojo.require("dojo.animation.Animation");


Property changes on: trunk/PhotoOp/root/static/js/src/animation.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/behavior.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/behavior.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/behavior.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,248 @@
+/*
+	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.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<nodes.length; x++){
+				if(nodes[x].id != ""){
+					removed.push(nodes[x]);
+					nodes.splice(x, 1);
+					x--;
+				}
+			}
+		}
+		var tElem = dojo.byId(id);
+		while(tElem){
+			if(!tElem["idcached"]){
+				added.push(tElem);
+			}
+			tElem.id = "";
+			tElem = dojo.byId(id);
+		}
+		this.matchCache[id] = this.matchCache[id].concat(added);
+		dojo.lang.forEach(this.matchCache[id], function(node){
+			node.id = id;
+			node.idcached = true;
+		});
+		return { "removed": removed, "added": added, "match": this.matchCache[id] };
+	}
+
+	this.applyToNode = function(node, action, ruleSetName){
+		if(typeof action == "string"){
+			dojo.event.topic.registerPublisher(action, node, ruleSetName);
+		}else if(typeof action == "function"){
+			if(ruleSetName == "found"){
+				action(node);
+			}else{
+				dojo.event.connect(node, ruleSetName, action);
+			}
+		}else{
+			action.srcObj = node;
+			action.srcFunc = ruleSetName;
+			dojo.event.kwConnect(action);
+		}
+	}
+
+	this.applyFast = function(){
+		dojo.profile.start("dojo.behavior.applyFast");
+		// fast DOM queries...wheeee!
+		forIn(this.behaviors, function(tBehavior, id){
+			var elems = dojo.behavior.elementsById(id);
+			dojo.lang.forEach(elems.added, 
+				function(elem){
+					forIn(tBehavior, function(ruleSet, ruleSetName){
+						if(dojo.lang.isArray(ruleSet)){
+							dojo.lang.forEach(ruleSet, function(action){
+								dojo.behavior.applyToNode(elem, action, ruleSetName);
+							});
+						}
+					});
+				}
+			);
+		});
+		dojo.profile.end("dojo.behavior.applyFast");
+	}
+	
+	this.applySlow = function(){
+		// iterate. Ugg.
+		dojo.profile.start("dojo.behavior.applySlow");
+		var all = document.getElementsByTagName("*");
+		var allLen = all.length;
+		for(var x=0; x<allLen; x++){
+			var elem = all[x];
+			if((elem.id)&&(!elem["behaviorAdded"])&&(this.behaviors[elem.id])){
+				elem["behaviorAdded"] = true;
+				forIn(this.behaviors[elem.id], function(ruleSet, ruleSetName){
+					if(dojo.lang.isArray(ruleSet)){
+						dojo.lang.forEach(ruleSet, function(action){
+							dojo.behavior.applyToNode(elem, action, ruleSetName);
+						});
+					}
+				});
+			}
+		}
+		dojo.profile.end("dojo.behavior.applySlow");
+	}
+}
+
+dojo.addOnLoad(dojo.behavior, "apply");


Property changes on: trunk/PhotoOp/root/static/js/src/behavior.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/bootstrap1.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/bootstrap1.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/bootstrap1.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,339 @@
+/**
+* @file bootstrap1.js
+*
+* summary: First file that is loaded that 'bootstraps' the entire dojo library suite.
+* note:  Must run before hostenv_*.js file.
+*
+* @author  Copyright 2004 Mark D. Anderson (mda at discerning.com)
+* TODOC: should the copyright be changed to Dojo Foundation?
+* @license Licensed under the Academic Free License 2.1 http://www.opensource.org/licenses/afl-2.1.php
+*
+* $Id: bootstrap1.js 4342 2006-06-11 23:03:30Z alex $
+*/
+
+// TODOC: HOW TO DOC THE BELOW?
+// @global: djConfig
+// summary:  
+//		Application code can set the global 'djConfig' prior to loading
+//		the library to override certain global settings for how dojo works.  
+// description:  The variables that can be set are as follows:
+//			- isDebug: false
+//			- allowQueryConfig: false
+//			- baseScriptUri: ""
+//			- baseRelativePath: ""
+//			- libraryScriptUri: ""
+//			- iePreventClobber: false
+//			- ieClobberMinimal: true
+//			- preventBackButtonFix: true
+//			- searchIds: []
+//			- parseWidgets: true
+// TODOC: HOW TO DOC THESE VARIABLES?
+// TODOC: IS THIS A COMPLETE LIST?
+// note:
+//		'djConfig' does not exist under 'dojo.*' so that it can be set before the 
+//		'dojo' variable exists.  
+// note:
+//		Setting any of these variables *after* the library has loaded does nothing at all. 
+// TODOC: is this still true?  Release notes for 0.3 indicated they could be set after load.
+//
+
+
+
+//TODOC:  HOW TO DOC THIS?
+// @global: dj_global
+// summary: 
+//		an alias for the top-level global object in the host environment
+//		(e.g., the window object in a browser).
+// description:  
+//		Refer to 'dj_global' rather than referring to window to ensure your
+//		code runs correctly in contexts other than web browsers (eg: Rhino on a server).
+var dj_global = this;
+
+
+
+function dj_undef(/*String*/ name, /*Object?*/ object){
+	//summary: Returns true if 'name' is defined on 'object' (or globally if 'object' is null).
+	//description: Note that 'defined' and 'exists' are not the same concept.
+	if(object==null){ object = dj_global; }
+	// exception if object is not an Object
+	return (typeof object[name] == "undefined");	// Boolean
+}
+
+
+// make sure djConfig is defined
+if(dj_undef("djConfig")){ 
+	var djConfig = {}; 
+}
+
+
+//TODOC:  HOW TO DOC THIS?
+// dojo is the root variable of (almost all) our public symbols -- make sure it is defined.
+if(dj_undef("dojo")){ 
+	var dojo = {}; 
+}
+
+//TODOC:  HOW TO DOC THIS?
+dojo.version = {
+	// summary: version number of this instance of dojo.
+	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 + ")";	// String
+		}
+	}
+}
+
+dojo.evalProp = function(/*String*/ name, /*Object*/ object, /*Boolean?*/ create){
+	// summary: Returns 'object[name]'.  If not defined and 'create' is true, will return a new Object.
+	// description: 
+	//		Returns null if 'object[name]' is not defined and 'create' is not true.
+	// 		Note: 'defined' and 'exists' are not the same concept.	
+	return (object && !dj_undef(name, object) ? object[name] : (create ? (object[name]={}) : undefined));	// mixed
+}
+
+
+dojo.parseObjPath = function(/*String*/ path, /*Object?*/ context, /*Boolean?*/ create){
+	// summary: Parse string path to an object, and return corresponding object reference and property name.
+	// description: 
+	//		Returns an object with two properties, 'obj' and 'prop'.  
+	//		'obj[prop]' is the reference indicated by 'path'.
+	// path: Path to an object, in the form "A.B.C".
+	// context: Object to use as root of path.  Defaults to 'dj_global'.
+	// create: If true, Objects will be created at any point along the 'path' that is undefined.
+	var object = (context != null ? context : dj_global);
+	var names = path.split('.');
+	var prop = names.pop();
+	for (var i=0,l=names.length;i<l && object;i++){
+		object = dojo.evalProp(names[i], object, create);
+	}
+	return {obj: object, prop: prop};	// Object: {obj: Object, prop: String}
+}
+
+
+dojo.evalObjPath = function(/*String*/ path, /*Boolean?*/ create){
+	// summary: Return the value of object at 'path' in the global scope, without using 'eval()'.
+	// path: Path to an object, in the form "A.B.C".
+	// create: If true, Objects will be created at any point along the 'path' that is undefined.
+	if(typeof path != "string"){ 
+		return dj_global; 
+	}
+	// fast path for no periods
+	if(path.indexOf('.') == -1){
+		return dojo.evalProp(path, dj_global, create);		// mixed
+	}
+
+	//MOW: old 'with' syntax was confusing and would throw an error if parseObjPath returned null.
+	var ref = dojo.parseObjPath(path, dj_global, create);
+	if(ref){
+		return dojo.evalProp(ref.prop, ref.obj, create);	// mixed
+	}
+	return null;
+}
+
+// ****************************************************************
+// global public utils
+// TODOC: DO WE WANT TO NOTE THAT THESE ARE GLOBAL PUBLIC UTILS?
+// ****************************************************************
+
+dojo.errorToString = function(/*Error*/ exception){
+	// summary: Return an exception's 'message', 'description' or text.
+
+	// TODO: overriding Error.prototype.toString won't accomplish this?
+ 	// 		... since natively generated Error objects do not always reflect such things?
+	if(!dj_undef("message", exception)){
+		return exception.message;		// String
+	}else if(!dj_undef("description", exception)){
+		return exception.description;	// String
+	}else{
+		return exception;				// Error
+	}
+}
+
+
+dojo.raise = function(/*String*/ message, /*Error?*/ exception){
+	// summary: Throw an error message, appending text of 'exception' if provided.
+	// note: Also prints a message to the user using 'dojo.hostenv.println'.
+	if(exception){
+		message = message + ": "+dojo.errorToString(exception);
+	}
+
+	// print the message to the user if hostenv.println is defined
+	try {	dojo.hostenv.println("FATAL: "+message); } catch (e) {}
+
+	throw Error(message);
+}
+
+//Stub functions so things don't break.
+//TODOC:  HOW TO DOC THESE?
+dojo.debug = function(){}
+dojo.debugShallow = function(obj){}
+dojo.profile = { start: function(){}, end: function(){}, stop: function(){}, dump: function(){} };
+
+
+function dj_eval(/*String*/ scriptFragment){ 
+	// summary: Perform an evaluation in the global scope.  Use this rather than calling 'eval()' directly.
+	// description: Placed in a separate function to minimize size of trapped evaluation context.
+	// note:
+	//	 - JSC eval() takes an optional second argument which can be 'unsafe'.
+	//	 - Mozilla/SpiderMonkey eval() takes an optional second argument which is the
+	//  	 scope object for new symbols.
+	return dj_global.eval ? dj_global.eval(scriptFragment) : eval(scriptFragment); 	// mixed
+}
+
+
+
+dojo.unimplemented = function(/*String*/ funcname, /*String?*/ extra){
+	// summary: Throw an exception because some function is not implemented.
+	// extra: Text to append to the exception message.
+	var message = "'" + funcname + "' not implemented";
+	if (extra != null) { message += " " + extra; }
+	dojo.raise(message);
+}
+
+
+dojo.deprecated = function(/*String*/ behaviour, /*String?*/ extra, /*String?*/ removal){
+	// summary: Log a debug message to indicate that a behavior has been deprecated.
+	// extra: Text to append to the message.
+	// removal: Text to indicate when in the future the behavior will be removed.
+	var message = "DEPRECATED: " + behaviour;
+	if(extra){ message += " " + extra; }
+	if(removal){ message += " -- will be removed in version: " + removal; }
+	dojo.debug(message);
+}
+
+
+
+dojo.inherits = function(/*Function*/ subclass, /*Function*/ superclass){
+	// summary: Set up inheritance between two classes.
+	if(typeof superclass != 'function'){ 
+		dojo.raise("dojo.inherits: superclass argument ["+superclass+"] must be a function (subclass: [" + subclass + "']");
+	}
+	subclass.prototype = new superclass();
+	subclass.prototype.constructor = subclass;
+	subclass.superclass = superclass.prototype;
+	// DEPRICATED: super is a reserved word, use 'superclass'
+	subclass['super'] = superclass.prototype;
+}
+
+dojo.render = (function(){
+	//TODOC: HOW TO DOC THIS?
+	// summary: Details rendering support, OS and browser of the current environment.
+	// TODOC: is this something many folks will interact with?  If so, we should doc the structure created...
+	function vscaffold(prefs, names){
+		var tmp = {
+			capable: false,
+			support: {
+				builtin: false,
+				plugin: false
+			},
+			prefixes: prefs
+		};
+		for(var prop in names){
+			tmp[prop] = false;
+		}
+		return tmp;
+	}
+
+	return {
+		name: "",
+		ver: dojo.version,
+		os: { win: false, linux: false, osx: false },
+		html: vscaffold(["html"], ["ie", "opera", "khtml", "safari", "moz"]),
+		svg: vscaffold(["svg"], ["corel", "adobe", "batik"]),
+		vml: vscaffold(["vml"], ["ie"]),
+		swf: vscaffold(["Swf", "Flash", "Mm"], ["mm"]),
+		swt: vscaffold(["Swt"], ["ibm"])
+	};
+})();
+
+// ****************************************************************
+// dojo.hostenv methods that must be defined in hostenv_*.js
+// ****************************************************************
+
+/**
+ * The interface definining the interaction with the EcmaScript host environment.
+*/
+
+/*
+ * None of these methods should ever be called directly by library users.
+ * Instead public methods such as loadModule should be called instead.
+ */
+dojo.hostenv = (function(){
+	// TODOC:  HOW TO DOC THIS?
+	// summary: Provides encapsulation of behavior that changes across different 'host environments' 
+	//			(different browsers, server via Rhino, etc).
+	// description: None of these methods should ever be called directly by library users.
+	//				Use public methods such as 'loadModule' instead.
+	
+	// default configuration options
+	var config = {
+		isDebug: false,
+		allowQueryConfig: false,
+		baseScriptUri: "",
+		baseRelativePath: "",
+		libraryScriptUri: "",
+		iePreventClobber: false,
+		ieClobberMinimal: true,
+		preventBackButtonFix: true,
+		searchIds: [],
+		parseWidgets: true
+	};
+
+	if (typeof djConfig == "undefined") { djConfig = config; }
+	else {
+		for (var option in config) {
+			if (typeof djConfig[option] == "undefined") {
+				djConfig[option] = config[option];
+			}
+		}
+	}
+
+	return {
+		name_: '(unset)',
+		version_: '(unset)',
+
+
+		getName: function(){ 
+			// sumary: Return the name of the host environment.
+			return this.name_; 	// String
+		},
+
+
+		getVersion: function(){ 
+			// summary: Return the version of the hostenv.
+			return this.version_; // String
+		},
+
+		getText: function(/*String*/ uri){
+			// summary:	Read the plain/text contents at the specified 'uri'.
+			// description: 
+			//			If 'getText()' is not implemented, then it is necessary to override 
+			//			'loadUri()' with an implementation that doesn't rely on it.
+
+			dojo.unimplemented('getText', "uri=" + uri);
+		}
+	};
+})();
+
+
+dojo.hostenv.getBaseScriptUri = function(){
+	// summary: Return the base script uri that other scripts are found relative to.
+	// TODOC: HUH?  This comment means nothing to me.  What other scripts? Is this the path to other dojo libraries?
+	//		MAYBE:  Return the base uri to scripts in the dojo library.	 ???
+	// return: Empty string or a path ending in '/'.
+	if(djConfig.baseScriptUri.length){ 
+		return djConfig.baseScriptUri;
+	}
+
+	// MOW: Why not:
+	//			uri = djConfig.libraryScriptUri || djConfig.baseRelativePath
+	//		??? Why 'new String(...)'
+	var uri = new String(djConfig.libraryScriptUri||djConfig.baseRelativePath);
+	if (!uri) { dojo.raise("Nothing returned by getLibraryScriptUri(): " + uri); }
+
+	// MOW: uri seems to not be actually used.  Seems to be hard-coding to djConfig.baseRelativePath... ???
+	var lastslash = uri.lastIndexOf('/');		// MOW ???
+	djConfig.baseScriptUri = djConfig.baseRelativePath;
+	return djConfig.baseScriptUri;	// String
+}


Property changes on: trunk/PhotoOp/root/static/js/src/bootstrap1.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/bootstrap2.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/bootstrap2.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/bootstrap2.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,174 @@
+/*
+	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
+*/
+
+//Semicolon is for when this file is integrated with a custom build on one line
+//with some other file's contents. Sometimes that makes things not get defined
+//properly, particularly with the using the closure below to do all the work.
+;(function(){
+	//Don't do this work if dojo.js has already done it.
+	if(typeof dj_usingBootstrap != "undefined"){
+		return;
+	}
+
+	var isRhino = false;
+	var isSpidermonkey = false;
+	var isDashboard = false;
+	if((typeof this["load"] == "function")&&((typeof this["Packages"] == "function")||(typeof this["Packages"] == "object"))){
+		isRhino = true;
+	}else if(typeof this["load"] == "function"){
+		isSpidermonkey  = true;
+	}else if(window.widget){
+		isDashboard = true;
+	}
+
+	var tmps = [];
+	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 = djConfig["baseScriptUri"];
+	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("<scr"+"ipt type='text/javascript' src='"+spath+"'></scr"+"ipt>");
+			} catch (e) {
+				var script = document.createElement("script");
+				script.src = spath;
+				document.getElementsByTagName("head")[0].appendChild(script);
+			}
+		}
+	}
+})();
+
+// Localization routines
+
+/**
+ * The locale to look for string bundles if none are defined for your locale.  Translations for all strings
+ * should be provided in this locale.
+ */
+//TODO: this really belongs in translation metadata, not in code
+dojo.fallback_locale = 'en';
+
+/**
+ * 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;
+};
+
+/**
+ * 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:
+ *
+ * <package>/
+ *  nls/
+ *   de/
+ *    mybundle.js
+ *   de-at/
+ *    mybundle.js
+ *   en/
+ *    mybundle.js
+ *   en-us/
+ *    mybundle.js
+ *   en-gb/
+ *    mybundle.js
+ *   es/
+ *    mybundle.js
+ *  ...etc
+ *
+ * where package is part of the namespace as used by dojo.require().  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, string bundles will be loaded for that locale and all general locales above it, as well
+ * as a system-specified fallback.  For example, "de_at" will also load "de" and "en".  Lookups will traverse
+ * the locales in this order.  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*/){
+
+	dojo.debug("EXPERIMENTAL: dojo.requireLocalization"); //dojo.experimental
+
+	var syms = dojo.hostenv.getModuleSymbols(modulename);
+	var modpath = syms.concat("nls").join("/");
+
+	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('-'));
+	}
+	if(searchlist[searchlist.length-1] != dojo.fallback_locale){
+		searchlist.push(dojo.fallback_locale);
+	}
+
+	var bundlepackage = [modulename, "_nls", bundlename].join(".");
+	var bundle = dojo.hostenv.startPackage(bundlepackage);
+	dojo.hostenv.loaded_modules_[bundlepackage] = bundle;
+	
+	var inherit = false;
+	for(var i = searchlist.length - 1; i >= 0; i--){
+		var loc = searchlist[i];
+		var pkg = [bundlepackage, loc].join(".");
+		var loaded = false;
+		if(!dojo.hostenv.findModule(pkg)){
+			// Mark loaded whether it's found or not, so that further load attempts will not be made
+			dojo.hostenv.loaded_modules_[pkg] = null;
+
+			var filespec = [modpath, loc, bundlename].join("/") + '.js';
+			loaded = dojo.hostenv.loadPath(filespec, null, function(hash) {
+ 				bundle[loc] = hash;
+ 				if(inherit){
+					// Use mixins approach to copy string references from inherit bundle, but skip overrides.
+					for(var x in inherit){
+						if(!bundle[loc][x]){
+							bundle[loc][x] = inherit[x];
+						}
+					}
+ 				}
+/*
+				// Use prototype to point to other bundle, then copy in result from loadPath
+				bundle[loc] = new function(){};
+				if(inherit){ bundle[loc].prototype = inherit; }
+				for(var i in hash){ bundle[loc][i] = hash[i]; }
+*/
+			});
+		}else{
+			loaded = true;
+		}
+		if(loaded && bundle[loc]){
+			inherit = bundle[loc];
+		}
+	}
+};


Property changes on: trunk/PhotoOp/root/static/js/src/bootstrap2.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/browser_debug.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/browser_debug.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/browser_debug.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,162 @@
+/*
+	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.hostenv.loadedUris.push("../src/bootstrap1.js");
+dojo.hostenv.loadedUris.push("../src/loader.js");
+dojo.hostenv.loadedUris.push("../src/hostenv_browser.js");
+dojo.hostenv.loadedUris.push("../src/bootstrap2.js");
+
+function removeComments(contents){
+	contents = new String((!contents) ? "" : contents);
+	// clobber all comments
+	contents = contents.replace( /^(.*?)\/\/(.*)$/mg , "$1");
+	contents = contents.replace( /(\n)/mg , "__DOJONEWLINE");
+	contents = contents.replace( /\/\*(.*?)\*\//g , "");
+	return contents.replace( /__DOJONEWLINE/mg , "\n");
+}
+
+dojo.hostenv.getRequiresAndProvides = function(contents){
+	// FIXME: should probably memoize this!
+	if(!contents){ return []; }
+	
+
+	// check to see if we need to load anything else first. Ugg.
+	var deps = [];
+	var tmp;
+	RegExp.lastIndex = 0;
+	var testExp = /dojo.(hostenv.loadModule|hosetnv.require|require|requireIf|kwCompoundRequire|hostenv.conditionalLoadModule|hostenv.startPackage|provide)\([\w\W]*?\)/mg;
+	while((tmp = testExp.exec(contents)) != null){
+		deps.push(tmp[0]);
+	}
+	return deps;
+}
+
+dojo.hostenv.getDelayRequiresAndProvides = function(contents){
+	// FIXME: should probably memoize this!
+	if(!contents){ return []; }
+
+	// check to see if we need to load anything else first. Ugg.
+	var deps = [];
+	var tmp;
+	RegExp.lastIndex = 0;
+	var testExp = /dojo.(requireAfterIf)\([\w\W]*?\)/mg;
+	while((tmp = testExp.exec(contents)) != null){
+		deps.push(tmp[0]);
+	}
+	return deps;
+}
+
+/*
+dojo.getNonExistantDescendants = function(objpath){
+	var ret = [];
+	// fast path for no periods
+	if(typeof objpath != "string"){ return dj_global; }
+	if(objpath.indexOf('.') == -1){
+		if(dj_undef(objpath, dj_global)){
+			ret.push[objpath];
+		}
+		return ret;
+	}
+
+	var syms = objpath.split(/\./);
+	var obj = dj_global;
+	for(var i=0;i<syms.length;++i){
+		if(dj_undef(syms[i], obj)){
+			for(var j=i; j<syms.length; j++){
+				ret.push(syms.slice(0, j+1).join("."));
+			}
+			break;
+		}
+	}
+	return ret;
+}
+*/
+
+dojo.clobberLastObject = function(objpath){
+	if(objpath.indexOf('.') == -1){
+		if(!dj_undef(objpath, dj_global)){
+			delete dj_global[objpath];
+		}
+		return true;
+	}
+
+	var syms = objpath.split(/\./);
+	var base = dojo.evalObjPath(syms.slice(0, -1).join("."), false);
+	var child = syms[syms.length-1];
+	if(!dj_undef(child, base)){
+		// alert(objpath);
+		delete base[child];
+		return true;
+	}
+	return false;
+}
+
+var removals = [];
+
+function zip(arr){
+	var ret = [];
+	var seen = {};
+	for(var x=0; x<arr.length; x++){
+		if(!seen[arr[x]]){
+			ret.push(arr[x]);
+			seen[arr[x]] = true;
+		}
+	}
+	return ret;
+}
+
+// over-write dj_eval to prevent actual loading of subsequent files
+var old_dj_eval = dj_eval;
+dj_eval = function(){ return true; }
+dojo.hostenv.oldLoadUri = dojo.hostenv.loadUri;
+dojo.hostenv.loadUri = function(uri){
+	if(dojo.hostenv.loadedUris[uri]){
+		return true; // fixes endless recursion opera trac 471
+	}
+	try{
+		var text = this.getText(uri, null, true);
+		var requires = dojo.hostenv.getRequiresAndProvides(text);
+		eval(requires.join(";"));
+		dojo.hostenv.loadedUris.push(uri);
+		dojo.hostenv.loadedUris[uri] = true;
+		var delayRequires = dojo.hostenv.getDelayRequiresAndProvides(text);
+		eval(delayRequires.join(";"));
+	}catch(e){ 
+		alert(e);
+	}
+	return true;
+}
+
+dojo.hostenv.writeIncludes = function(){
+	for(var x=removals.length-1; x>=0; x--){
+		dojo.clobberLastObject(removals[x]);
+	}
+	var depList = [];
+	var seen = {};
+	for(var x=0; x<dojo.hostenv.loadedUris.length; x++){
+		var curi = dojo.hostenv.loadedUris[x];
+		// dojo.debug(curi);
+		if(!seen[curi]){
+			seen[curi] = true;
+			depList.push(curi);
+		}
+	}
+
+	dojo.hostenv._global_omit_module_check = true;
+	for(var x=4; x<depList.length; x++){
+		document.write("<script type='text/javascript' src='"+depList[x]+"'></script>");
+	}
+	document.write("<script type='text/javascript'>dojo.hostenv._global_omit_module_check = false;</script>");
+
+	// 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;
+}


Property changes on: trunk/PhotoOp/root/static/js/src/browser_debug.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/collections/ArrayList.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/collections/ArrayList.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/collections/ArrayList.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,146 @@
+/*
+	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.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<a.length; i++){
+				items.push(a[i]);
+			}
+			this.count=items.length;
+		}
+	};
+	this.clear=function(){
+		//	summary
+		//	Clear all elements out of the collection, and reset the count.
+		items.splice(0, items.length);
+		this.count=0;
+	};
+	this.clone=function(){
+		//	summary
+		//	Clone the array list
+		return new dojo.collections.ArrayList(items);	//	dojo.collections.ArrayList
+	};
+	this.contains=function(/* object */obj){
+		//	summary
+		//	Check to see if the passed object is a member in the ArrayList
+		for(var i=0; i < items.length; i++){
+			if(items[i] == obj) {
+				return true;	//	bool
+			}
+		}
+		return false;	//	bool
+	};
+	this.forEach=function(/* function */ fn, /* object? */ scope){
+		//	summary
+		//	functional iterator, following the mozilla spec.
+		var s=scope||dj_global;
+		if(Array.forEach){
+			Array.forEach(items, fn, s);
+		}else{
+			for(var i=0; i<items.length; i++){
+				fn.call(s, items[i], i, items);
+			}
+		}
+	};
+	this.getIterator=function(){
+		//	summary
+		//	Get an Iterator for this object
+		return new dojo.collections.Iterator(items);	//	dojo.collections.Iterator
+	};
+	this.indexOf=function(/* object */obj){
+		//	summary
+		//	Return the numeric index of the passed object; will return -1 if not found.
+		for(var i=0; i < items.length; i++){
+			if(items[i] == obj) {
+				return i;	//	int
+			}
+		}
+		return -1;	// int
+	};
+	this.insert=function(/* int */ i, /* object */ obj){
+		//	summary
+		//	Insert the passed object at index i
+		items.splice(i,0,obj);
+		this.count=items.length;
+	};
+	this.item=function(/* int */ i){
+		//	summary
+		//	return the element at index i
+		return items[i];	//	object
+	};
+	this.remove=function(/* object */obj){
+		//	summary
+		//	Look for the passed object, and if found, remove it from the internal array.
+		var i=this.indexOf(obj);
+		if(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||","));
+	};
+};


Property changes on: trunk/PhotoOp/root/static/js/src/collections/ArrayList.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/collections/BinaryTree.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/collections/BinaryTree.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/collections/BinaryTree.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,203 @@
+/*
+	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.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
+};


Property changes on: trunk/PhotoOp/root/static/js/src/collections/BinaryTree.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/collections/Collections.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/collections/Collections.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/collections/Collections.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,125 @@
+/*
+	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.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; i++){
+				arr.push(fn.call(s,a[i]));
+			}
+			return arr;		//	array
+		}
+	};
+	this.reset=function(){
+		//	summary
+		//	reset the internal cursor.
+		position=0;
+		this.element=a[position];
+	};
+}
+
+/*	Notes:
+ *	The DictionaryIterator no longer supports a key and value property;
+ *	the reality is that you can use this to iterate over a JS object
+ *	being used as a hashtable.
+ */
+dojo.collections.DictionaryIterator=function(/* object */obj){
+	//	summary
+	//	return an object of type dojo.collections.DictionaryIterator
+	var a=[];	//	Create an indexing array
+	var testObject={};
+	for(var p in obj){
+		if(!testObject[p]){
+			a.push(obj[p]);	//	fill it up
+		}
+	}
+	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; i++){
+				arr.push(fn.call(s,a[i]));
+			}
+			return arr;		//	array
+		}
+	};
+	this.reset=function() { 
+		//	summary
+		//	reset the internal cursor.
+		position=0; 
+		this.element=a[position];
+	};
+};


Property changes on: trunk/PhotoOp/root/static/js/src/collections/Collections.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/collections/Dictionary.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/collections/Dictionary.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/collections/Dictionary.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,129 @@
+/*
+	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.collections.Dictionary");
+dojo.require("dojo.collections.Collections");
+
+dojo.collections.Dictionary=function(/* dojo.collections.Dictionary? */dictionary){
+	//	summary
+	//	Returns an object of type dojo.collections.Dictionary
+	var items={};
+	this.count=0;
+
+	//	comparator for property addition and access.
+	var testObject={};
+
+	this.add=function(/* string */k, /* object */v){
+		//	summary
+		//	Add a new item to the Dictionary.
+		var b=(k in items);
+		items[k]=new dojo.collections.DictionaryEntry(k,v);
+		if(!b){
+			this.count++;
+		}
+	};
+	this.clear=function(){
+		//	summary
+		//	Clears the internal dictionary.
+		items={};
+		this.count=0;
+	};
+	this.clone=function(){
+		//	summary
+		//	Returns a new instance of dojo.collections.Dictionary; note the the dictionary is a clone but items might not be.
+		return new dojo.collections.Dictionary(this);	//	dojo.collections.Dictionary
+	};
+	this.contains=this.containsKey=function(/* string */k){
+		//	summary
+		//	Check to see if the dictionary has an entry at key "k".
+		if(testObject[k]){
+			return false;			// bool
+		}
+		return (items[k]!=null);	//	bool
+	};
+	this.containsValue=function(/* object */v){
+		//	summary
+		//	Check to see if the dictionary has an entry with value "v".
+		var e=this.getIterator();
+		while(e.get()){
+			if(e.element.value==v){
+				return true;	//	bool
+			}
+		}
+		return false;	//	bool
+	};
+	this.entry=function(/* string */k){
+		//	summary
+		//	Accessor method; similar to dojo.collections.Dictionary.item but returns the actual Entry object.
+		return items[k];	//	dojo.collections.DictionaryEntry
+	};
+	this.forEach=function(/* function */ fn, /* object? */ scope){
+		//	summary
+		//	functional iterator, following the mozilla spec.
+		var a=[];	//	Create an indexing array
+		for(var p in items) {
+			if(!testObject[p]){
+				a.push(items[p]);	//	fill it up
+			}
+		}
+		var s=scope||dj_global;
+		if(Array.forEach){
+			Array.forEach(a, fn, s);
+		}else{
+			for(var i=0; i<a.length; i++){
+				fn.call(s, a[i], i, a);
+			}
+		}
+	};
+	this.getKeyList=function(){
+		//	summary
+		//	Returns an array of the keys in the dictionary.
+		return (this.getIterator()).map(function(entry){ 
+			return entry.key; 
+		});	//	array
+	};
+	this.getValueList=function(){
+		//	summary
+		//	Returns an array of the values in the dictionary.
+		return (this.getIterator()).map(function(entry){ 
+			return entry.value; 
+		});	//	array
+	};
+	this.item=function(/* string */k){
+		//	summary
+		//	Accessor method.
+		if(k in items){
+			return items[k].valueOf();	//	object
+		}
+		return undefined;	//	object
+	};
+	this.getIterator=function(){
+		//	summary
+		//	Gets a dojo.collections.DictionaryIterator for iteration purposes.
+		return new dojo.collections.DictionaryIterator(items);	//	dojo.collections.DictionaryIterator
+	};
+	this.remove=function(/* string */k){
+		//	summary
+		//	Removes the item at k from the internal collection.
+		if(k in items && !testObject[k]){
+			delete items[k];
+			this.count--;
+			return true;	//	bool
+		}
+		return false;	//	bool
+	};
+
+	if (dictionary){
+		var e=dictionary.getIterator();
+		while(e.get()) {
+			 this.add(e.element.key, e.element.value);
+		}
+	}
+};


Property changes on: trunk/PhotoOp/root/static/js/src/collections/Dictionary.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/collections/Graph.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/collections/Graph.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/collections/Graph.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,153 @@
+/*
+	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.collections.Graph");
+dojo.require("dojo.collections.Collections");
+
+dojo.experimental("dojo.collections.Graph");
+
+dojo.collections.Graph=function(nodes){
+	function node(key, data, neighbors) {
+		this.key=key;
+		this.data=data;
+		this.neighbors=neighbors||new adjacencyList();
+		this.addDirected=function(){
+			if (arguments[0].constructor==edgeToNeighbor){
+				this.neighbors.add(arguments[0]);
+			}else{
+				var n=arguments[0];
+				var cost=arguments[1]||0;
+				this.neighbors.add(new edgeToNeighbor(n, cost));
+			}
+		}
+	}
+	function nodeList(){
+		var d=new dojo.collections.Dictionary();
+		function nodelistiterator(){
+			var o=[] ;	//	Create an indexing array
+			var e=d.getIterator();
+			while(e.get()){
+				o[o.length]=e.element;
+			}
+
+			var position=0;
+			this.element=o[position]||null;
+			this.atEnd=function(){
+				return (position>=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<o.length; i++){
+						arr.push(fn.call(s,o[i]));
+					}
+					return arr;		//	array
+				}
+			};
+			this.reset=function(){
+				position=0;
+				this.element=o[position];
+			};
+		}
+		
+		this.add=function(node){
+			d.add(node.key, node);
+		};
+		this.clear=function(){
+			d.clear();
+		};
+		this.containsKey=function(key){
+			return d.containsKey(key);
+		};
+		this.getIterator=function(){
+			return new nodelistiterator(this);
+		};
+		this.item=function(key){
+			return d.item(key);
+		};
+		this.remove=function(node){
+			d.remove(node.key);
+		};
+	}
+	function edgeToNeighbor(node, cost){
+		this.neighbor=node;
+		this.cost=cost;
+	}
+	function adjacencyList(){
+		var d=[];
+		this.add=function(o){
+			d.push(o);
+		};
+		this.item=function(i){
+			return d[i];
+		};
+		this.getIterator=function(){
+			return new dojo.collections.Iterator([].concat(d));
+		};
+	}
+
+	this.nodes=nodes||new nodeList();
+	this.count=this.nodes.count;
+	this.clear=function(){
+		this.nodes.clear();
+		this.count=0;
+	};
+	this.addNode=function(){
+		var n=arguments[0];
+		if(arguments.length > 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);
+	};
+}


Property changes on: trunk/PhotoOp/root/static/js/src/collections/Graph.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/collections/Queue.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/collections/Queue.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/collections/Queue.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,87 @@
+/*
+	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.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<q.length; i++){
+			if (q[i]==o){
+				return true;	//	bool
+			}
+		}
+		return false;	//	bool
+	};
+	this.copyTo=function(/* array */ arr, /* int */ i){
+		//	summary
+		//	Copy the contents of this queue into the passed array at index i.
+		arr.splice(i,0,q);
+	};
+	this.dequeue=function(){
+		//	summary
+		//	shift the first element off the queue and return it
+		var r=q.shift();
+		this.count=q.length;
+		return r;	//	object
+	};
+	this.enqueue=function(/* object */ o){
+		//	summary
+		//	put the passed object at the end of the queue
+		this.count=q.push(o);
+	};
+	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<q.length; i++){
+				fn.call(s, q[i], i, q);
+			}
+		}
+	};
+	this.getIterator=function(){
+		//	summary
+		//	get an Iterator based on this queue.
+		return new dojo.collections.Iterator(q);	//	dojo.collections.Iterator
+	};
+	this.peek=function(){
+		//	summary
+		//	get the next element in the queue without altering the queue.
+		return q[0];
+	};
+	this.toArray=function(){
+		//	summary
+		//	return an array based on the internal array of the queue.
+		return [].concat(q);
+	};
+};


Property changes on: trunk/PhotoOp/root/static/js/src/collections/Queue.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/collections/Set.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/collections/Set.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/collections/Set.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,84 @@
+/*
+	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.collections.Set");
+dojo.require("dojo.collections.Collections");
+dojo.require("dojo.collections.ArrayList");
+
+//	straight up sets are based on arrays or array-based collections.
+dojo.collections.Set = new function(){
+	this.union = function(setA, setB){
+		if (setA.constructor == Array) var setA = new dojo.collections.ArrayList(setA);
+		if (setB.constructor == Array) var setB = new dojo.collections.ArrayList(setB);
+		if (!setA.toArray || !setB.toArray) dojo.raise("Set operations can only be performed on array-based collections.");
+		var result = new dojo.collections.ArrayList(setA.toArray());
+		var e = setB.getIterator();
+		while(!e.atEnd()){
+			var item=e.get();
+			if(!result.contains(item)){
+				result.add(item);
+			}
+		}
+		return result;
+	};
+	this.intersection = function(setA, setB){
+		if (setA.constructor == Array) var setA = new dojo.collections.ArrayList(setA);
+		if (setB.constructor == Array) var setB = new dojo.collections.ArrayList(setB);
+		if (!setA.toArray || !setB.toArray) dojo.raise("Set operations can only be performed on array-based collections.");
+		var result = new dojo.collections.ArrayList();
+		var e = setB.getIterator();
+		while(!e.atEnd()){
+			var item=e.get();
+			if(setA.contains(item)){
+				result.add(item);
+			}
+		}
+		return result;
+	};
+	//	returns everything in setA that is not in setB.
+	this.difference = function(setA, setB){
+		if (setA.constructor == Array) var setA = new dojo.collections.ArrayList(setA);
+		if (setB.constructor == Array) var setB = new dojo.collections.ArrayList(setB);
+		if (!setA.toArray || !setB.toArray) dojo.raise("Set operations can only be performed on array-based collections.");
+		var result = new dojo.collections.ArrayList();
+		var e=setA.getIterator();
+		while(!e.atEnd()){
+			var item=e.get();
+			if(!setB.contains(item)){
+				result.add(item);
+			}
+		}
+		return result;
+	};
+	this.isSubSet = function(setA, setB) {
+		if (setA.constructor == Array) var setA = new dojo.collections.ArrayList(setA);
+		if (setB.constructor == Array) var setB = new dojo.collections.ArrayList(setB);
+		if (!setA.toArray || !setB.toArray) dojo.raise("Set operations can only be performed on array-based collections.");
+		var e = setA.getIterator();
+		while(!e.atEnd()){
+			if(!setB.contains(e.get())){
+				return false;
+			}
+		}
+		return true;
+	};
+	this.isSuperSet = function(setA, setB){
+		if (setA.constructor == Array) var setA = new dojo.collections.ArrayList(setA);
+		if (setB.constructor == Array) var setB = new dojo.collections.ArrayList(setB);
+		if (!setA.toArray || !setB.toArray) dojo.raise("Set operations can only be performed on array-based collections.");
+		var e = setB.getIterator();
+		while(!e.atEnd()){
+			if(!setA.contains(e.get())){
+				return false;
+			}
+		}
+		return true;
+	};
+}();


Property changes on: trunk/PhotoOp/root/static/js/src/collections/Set.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/collections/SkipList.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/collections/SkipList.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/collections/SkipList.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,146 @@
+/*
+	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.collections.SkipList");
+dojo.require("dojo.collections.Collections");
+dojo.require("dojo.experimental");
+
+dojo.experimental("dojo.collections.SkipList");
+
+dojo.collections.SkipList = function(){
+	function node(height, val){
+		this.value = val;
+		this.height = height;
+		this.nodes = new nodeList(height);
+		this.compare = function(val){
+			if (this.value > 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.current = list.head;
+		this.atEnd = false;
+		this.moveNext = function(){
+			if (this.atEnd) return !this.atEnd;
+			this.current = this.current.nodes[0];
+			this.atEnd = (this.current == null);
+			return !this.atEnd;
+		};
+		this.reset = function(){
+			this.current = null;
+		};
+	}
+
+	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; 
+	};
+}


Property changes on: trunk/PhotoOp/root/static/js/src/collections/SkipList.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/collections/SortedList.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/collections/SortedList.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/collections/SortedList.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,211 @@
+/*
+	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.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<q.length; i++){
+				fn.call(s, q[i], i, q);
+			}
+		}
+	};
+	this.getByIndex=function(/* int */ i){
+		//	summary
+		//	return the item at index i
+		return q[i].valueOf();	//	object
+	};
+	this.getIterator=function(){
+		//	summary
+		//	get an iterator for this object
+		return new dojo.collections.DictionaryIterator(items);	//	dojo.collections.DictionaryIterator
+	};
+	this.getKey=function(/* int */ i){
+		//	summary
+		//	return the key of the item at index i
+		return q[i].key;
+	};
+	this.getKeyList=function(){
+		//	summary
+		//	return an array of the keys set in this list
+		var arr=[];
+		var e=this.getIterator();
+		while (!e.atEnd()){
+			arr.push(e.get().key);
+		}
+		return arr;	//	array
+	};
+	this.getValueList=function(){
+		//	summary
+		//	return an array of values in this list
+		var arr=[];
+		var e=this.getIterator();
+		while (!e.atEnd()){
+			arr.push(e.get().value);
+		}
+		return arr;	//	array
+	};
+	this.indexOfKey=function(/* string */ k){
+		//	summary
+		//	return the index of the passed key.
+		for (var i=0; i<q.length; i++){
+			if (q[i].key==k){
+				return i;	//	int
+			}
+		}
+		return -1;	//	int
+	};
+	this.indexOfValue=function(/* object */ o){
+		//	summary
+		//	return the first index of object o
+		for (var i=0; i<q.length; i++){
+			if (q[i].value==o){
+				return i;	//	int
+			}
+		}
+		return -1;	//	int
+	};
+	this.item=function(/* string */ k){
+		// 	summary
+		//	return the value of the object at location k.
+		if(k in items && !testObject[k]){
+			return items[k].valueOf();	//	object
+		}
+		return undefined;	//	object
+	};
+	this.remove=function(/* string */k){
+		// 	summary
+		//	remove the item at location k and rebuild the internal collections.
+		delete items[k];
+		build();
+		this.count=q.length;
+	};
+	this.removeAt=function(/* int */ i){
+		//	summary
+		//	remove the item at index i, and rebuild the internal collections.
+		delete items[q[i].key];
+		build();
+		this.count=q.length;
+	};
+	this.replace=function(/* string */ k, /* object */ v){
+		//	summary
+		//	Replace an existing item if it's there, and add a new one if not.
+		if (!items[k]){
+			//	we're adding a new object, return false
+			this.add(k,v);
+			return false; // bool
+		}else{
+			//	we're replacing an object, return true
+			items[k]=new dojo.collections.DictionaryEntry(k,v);
+			q.sort(sorter);
+			return true; // bool
+		}
+	};
+	this.setByIndex=function(/* int */ i, /* object */ o){
+		//	summary
+		//	set an item by index
+		items[q[i].key].value=o;
+		build();
+		this.count=q.length;
+	};
+	if (dictionary){
+		var e=dictionary.getIterator();
+		while (!e.atEnd()){
+			var item=e.get();
+			q[q.length]=items[item.key]=new dojo.collections.DictionaryEntry(item.key,item.value);
+		}
+		q.sort(sorter);
+	}
+}


Property changes on: trunk/PhotoOp/root/static/js/src/collections/SortedList.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/collections/Stack.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/collections/Stack.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/collections/Stack.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,85 @@
+/*
+	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.collections.Stack");
+dojo.require("dojo.collections.Collections");
+
+dojo.collections.Stack=function(/* array? */arr){
+	//	summary
+	//	returns an object of type dojo.collections.Stack
+	var q=[];
+	if (arr) q=q.concat(arr);
+	this.count=q.length;
+	this.clear=function(){
+		//	summary
+		//	Clear the internal array and reset the count
+		q=[];
+		this.count=q.length;
+	};
+	this.clone=function(){
+		//	summary
+		//	Create and return a clone of this Stack
+		return new dojo.collections.Stack(q);
+	};
+	this.contains=function(/* object */o){
+		//	summary
+		//	check to see if the stack contains object o
+		for (var i=0; i<q.length; i++){
+			if (q[i] == o){
+				return true;	//	bool
+			}
+		}
+		return false;	//	bool
+	};
+	this.copyTo=function(/* array */ arr, /* int */ i){
+		//	summary
+		//	copy the stack into array arr at index i
+		arr.splice(i,0,q);
+	};
+	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<q.length; i++){
+				fn.call(s, q[i], i, q);
+			}
+		}
+	};
+	this.getIterator=function(){
+		//	summary
+		//	get an iterator for this collection
+		return new dojo.collections.Iterator(q);	//	dojo.collections.Iterator
+	};
+	this.peek=function(){
+		//	summary
+		//	Return the next item without altering the stack itself.
+		return q[(q.length-1)];	//	object
+	};
+	this.pop=function(){
+		//	summary
+		//	pop and return the next item on the stack
+		var r=q.pop();
+		this.count=q.length;
+		return r;	//	object
+	};
+	this.push=function(/* object */ o){
+		//	summary
+		//	Push object o onto the stack
+		this.count=q.push(o);
+	};
+	this.toArray=function(){
+		//	summary
+		//	create and return an array based on the internal collection
+		return [].concat(q);	//	array
+	};
+}


Property changes on: trunk/PhotoOp/root/static/js/src/collections/Stack.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/collections/__package__.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/collections/__package__.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/collections/__package__.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,22 @@
+/*
+	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.kwCompoundRequire({
+	common: [
+		"dojo.collections.Collections",
+		"dojo.collections.SortedList", 
+		"dojo.collections.Dictionary", 
+		"dojo.collections.Queue", 
+		"dojo.collections.ArrayList", 
+		"dojo.collections.Stack",
+		"dojo.collections.Set"
+	]
+});
+dojo.provide("dojo.collections.*");


Property changes on: trunk/PhotoOp/root/static/js/src/collections/__package__.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/compat/0.2.2.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/compat/0.2.2.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/compat/0.2.2.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,75 @@
+/*
+	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
+*/
+
+/*
+Compatibility package to get 0.2.2 functionality in later Dojo releases.
+*/
+
+//**********************************
+//From bootstrap1.js
+dj_throw = dj_rethrow = function(m, e){
+	dojo.deprecated("dj_throw and dj_rethrow", "use dojo.raise instead", "0.4");
+	dojo.raise(m, e);
+}
+
+dj_debug = dojo.debug;
+dj_unimplemented = dojo.unimplemented;
+dj_deprecated = dojo.deprecated;
+
+dj_inherits = function(subclass, superclass){
+	dojo.deprecated("dj_inherits", "use dojo.inherits instead", "0.4");
+	dojo.inherits(subclass, superclass);
+}
+
+/**
+* Set the base script uri.
+*/
+// In JScript .NET, see interface System._AppDomain implemented by
+// System.AppDomain.CurrentDomain. Members include AppendPrivatePath,
+// RelativeSearchPath, BaseDirectory.
+dojo.hostenv.setBaseScriptUri = function(uri){ djConfig.baseScriptUri = uri }
+
+//**********************************
+//From the old bootstrap2.js
+dojo.hostenv.moduleLoaded = function(){
+	return dojo.hostenv.startPackage.apply(dojo.hostenv, arguments);
+}
+
+dojo.hostenv.require = dojo.hostenv.loadModule;
+dojo.requireAfter = dojo.require;
+dojo.conditionalRequire = dojo.requireIf;
+
+dojo.requireAll = function() {
+	for(var i = 0; i < arguments.length; i++) { dojo.require(arguments[i]); }
+}
+
+dojo.hostenv.conditionalLoadModule = function(){
+	dojo.kwCompoundRequire.apply(dojo, arguments);
+}
+
+dojo.hostenv.provide = dojo.hostenv.startPackage;
+
+//**********************************
+//From hostenv_browser.js
+dojo.hostenv.byId = dojo.byId;
+
+dojo.hostenv.byIdArray = dojo.byIdArray = function(){
+	var ids = [];
+	for(var i = 0; i < arguments.length; i++){
+		if((arguments[i] instanceof Array)||(typeof arguments[i] == "array")){
+			for(var j = 0; j < arguments[i].length; j++){
+				ids = ids.concat(dojo.hostenv.byIdArray(arguments[i][j]));
+			}
+		}else{
+			ids.push(dojo.hostenv.byId(arguments[i]));
+		}
+	}
+	return ids;
+}


Property changes on: trunk/PhotoOp/root/static/js/src/compat/0.2.2.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/crypto/Blowfish.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/crypto/Blowfish.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/crypto/Blowfish.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,548 @@
+/*
+	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.crypto");
+dojo.provide("dojo.crypto.Blowfish");
+
+/*	Blowfish
+ *	Created based on the C# implementation by Marcus Hahn (http://www.hotpixel.net/)
+ *	Unsigned math functions derived from Joe Gregorio's SecureSyndication GM script
+ *	http://bitworking.org/projects/securesyndication/
+ *	(Note that this is *not* an adaption of the above script)
+ *
+ *	version 1.0 
+ *	TRT 
+ *	2005-12-08
+ */
+dojo.crypto.Blowfish = new function(){
+	var POW2=Math.pow(2,2);
+	var POW3=Math.pow(2,3);
+	var POW4=Math.pow(2,4);
+	var POW8=Math.pow(2,8);
+	var POW16=Math.pow(2,16);
+	var POW24=Math.pow(2,24);
+	var iv=null;	//	CBC mode initialization vector
+	var boxes={
+		p:[
+			0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, 
+			0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, 
+			0x9216d5d9, 0x8979fb1b
+		],
+		s0:[
+			0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99,
+			0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e,
+			0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,
+			0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e,
+			0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440,
+			0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,
+			0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677,
+			0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032,
+			0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,
+			0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0,
+			0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, 
+			0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, 
+			0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, 
+			0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7,
+			0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,
+			0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09,
+			0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, 
+			0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, 
+			0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82, 
+			0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, 
+			0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
+			0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, 
+			0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0,
+			0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, 
+			0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1,
+			0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9,
+			0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,
+			0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af,
+			0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5,
+			0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
+			0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915,
+			0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a
+		],
+		s1:[
+			0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266,
+			0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e,
+			0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
+			0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1,
+			0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8,
+			0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,
+			0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7,
+			0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, 
+			0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, 
+			0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87,
+			0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, 
+			0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,
+			0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509, 
+			0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, 
+			0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,
+			0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960, 
+			0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28,
+			0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, 
+			0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf,
+			0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, 
+			0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, 
+			0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281, 
+			0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696,
+			0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
+			0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0, 
+			0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250,
+			0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,
+			0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061,
+			0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e,
+			0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,
+			0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340,
+			0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7
+		],
+		s2:[
+			0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068,
+			0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840,
+			0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,
+			0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb,
+			0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6,
+			0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
+			0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb,
+			0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b,
+			0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,
+			0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc,
+			0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564,
+			0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,
+			0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728,
+			0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e,
+			0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,
+			0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b,
+			0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb,
+			0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,
+			0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9,
+			0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe,
+			0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, 
+			0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61,
+			0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9,
+			0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, 
+			0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, 
+			0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, 
+			0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, 
+			0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62,
+			0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76,
+			0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, 
+			0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c,
+			0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0
+		],
+		s3:[
+			0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe,
+			0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4,
+			0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, 
+			0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22,
+			0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, 
+			0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, 
+			0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51,
+			0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, 
+			0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, 
+			0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd, 
+			0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319,
+			0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, 
+			0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32, 
+			0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, 
+			0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
+			0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47,
+			0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, 
+			0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,
+			0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, 
+			0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, 
+			0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, 
+			0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525, 
+			0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442,
+			0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, 
+			0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d,
+			0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, 
+			0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, 
+			0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, 
+			0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, 
+			0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,
+			0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9,
+			0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6
+		]
+	}
+////////////////////////////////////////////////////////////////////////////
+	function add(x,y){
+		var sum=(x+y)&0xffffffff;
+		if (sum<0){
+			sum=-sum;
+			return (0x10000*((sum>>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<k.length; i++) 
+				a.push(k.charCodeAt(i)&0xff);
+			k=a;
+		}
+		//	init the boxes
+		var box = { p:[], s0:[], s1:[], s2:[], s3:[] };
+		for(var i=0; i<boxes.p.length; i++) box.p.push(boxes.p[i]);
+		for(var i=0; i<boxes.s0.length; i++) box.s0.push(boxes.s0[i]);
+		for(var i=0; i<boxes.s1.length; i++) box.s1.push(boxes.s1[i]);
+		for(var i=0; i<boxes.s2.length; i++) box.s2.push(boxes.s2[i]);
+		for(var i=0; i<boxes.s3.length; i++) box.s3.push(boxes.s3[i]);
+
+		//	init p with the key
+		var pos=0;
+		var data=0;
+		for(var i=0; i < box.p.length; i++){
+			for (var j=0; j<4; j++){
+				data = (data*POW8) | k[pos];
+				if(++pos==k.length) pos=0;
+			}
+			box.p[i] = xor(box.p[i], data);
+		}
+
+		//	encrypt p and the s boxes
+		var res={ left:0, right:0 };
+		for(var i=0; i<box.p.length;){
+			eb(res, box);
+			box.p[i++]=res.left;
+			box.p[i++]=res.right;
+		}
+		for (var i=0; i<4; i++){
+			for(var j=0; j<box["s"+i].length;){
+				eb(res, box);
+				box["s"+i][j++]=res.left;
+				box["s"+i][j++]=res.right;
+			}
+		}
+		return box;
+	}
+
+////////////////////////////////////////////////////////////////////////////
+//	CONVERSION FUNCTIONS
+////////////////////////////////////////////////////////////////////////////
+	//	these operate on byte arrays, NOT word arrays.
+	function toBase64(ba){ 
+		var p="=";
+		var tab="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+		var s=[];
+		var count=0;
+		for (var i =0; i<ba.length;){
+			var t=ba[i++]<<16|ba[i++]<<8|ba[i++];
+			s.push(tab.charAt((t>>>18)&0x3f)); 
+			s.push(tab.charAt((t>>>12)&0x3f));
+			s.push(tab.charAt((t>>>6)&0x3f));
+			s.push(tab.charAt(t&0x3f));
+			count+=4;
+		}
+		var pa=i-ba.length;
+		while((pa--)>0)	s.push(p);	
+		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<l;){
+			var t=tab.indexOf(s[i++])<<18|tab.indexOf(s[i++])<<12|tab.indexOf(s[i++])<<6|tab.indexOf(s[i++]);
+			out.push((t>>>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<iv.length; i++)
+					s.push((iv[i]).toString(16));
+				return s.join("");
+			}
+			case dojo.crypto.outputTypes.String:{
+				return iv.join("");
+			}
+			case dojo.crypto.outputTypes.Raw:{
+				return iv;
+			}
+			default:{
+				return toBase64(iv);
+			}
+		}
+	};
+	this.setIV=function(data, inputType){
+		var ip=inputType||dojo.crypto.outputTypes.Base64;
+		var ba=null;
+		switch(ip){
+			case dojo.crypto.outputTypes.String:{
+				ba=[];
+				for (var i=0; i<data.length; i++){
+					ba.push(data.charCodeAt(i));
+				}
+				break;
+			}
+			case dojo.crypto.outputTypes.Hex:{
+				ba=[];
+				var i=0;
+				while (i+1<data.length){
+					ba.push(parseInt(data.substr(i,2),16));
+					i+=2;
+				}
+				break;
+			}
+			case dojo.crypto.outputTypes.Raw:{
+				ba=data;
+				break;
+			}
+			default:{
+				ba=fromBase64(data);
+				break;
+			}
+		}
+		//	make it a pair of words now
+		iv={};
+		iv.left=ba[0]*POW24|ba[1]*POW16|ba[2]*POW8|ba[3];
+		iv.right=ba[4]*POW24|ba[5]*POW16|ba[6]*POW8|ba[7];
+	}
+	this.encrypt = function(plaintext, key, ao){
+		var out=dojo.crypto.outputTypes.Base64;
+		var mode=dojo.crypto.cipherModes.EBC;
+		if (ao){
+			if (ao.outputType) out=ao.outputType;
+			if (ao.cipherMode) mode=ao.cipherMode;
+		}
+
+		var bx = init(key);
+		var padding = 8-(plaintext.length&7);
+		for (var i=0; i<padding; i++) plaintext+=String.fromCharCode(padding);
+		var cipher=[];
+		var count=plaintext.length >> 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<count; i++){
+			o.left=plaintext.charCodeAt(pos)*POW24
+				|plaintext.charCodeAt(pos+1)*POW16
+				|plaintext.charCodeAt(pos+2)*POW8
+				|plaintext.charCodeAt(pos+3);
+			o.right=plaintext.charCodeAt(pos+4)*POW24
+				|plaintext.charCodeAt(pos+5)*POW16
+				|plaintext.charCodeAt(pos+6)*POW8
+				|plaintext.charCodeAt(pos+7);
+
+			if(isCBC){
+				o.left=xor(o.left, vector.left);
+				o.right=xor(o.right, vector.right);
+			}
+
+			eb(o, bx);	//	encrypt the block
+
+			if(isCBC){
+				vector.left=o.left;
+				vector.right=o.right;dojo.crypto.outputTypes.Hex
+			}
+
+			cipher.push((o.left>>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<cipher.length; i++)
+					s.push((cipher[i]).toString(16));
+				return s.join("");
+			}
+			case dojo.crypto.outputTypes.String:{
+				return cipher.join("");
+			}
+			case dojo.crypto.outputTypes.Raw:{
+				return cipher;
+			}
+			default:{
+				return toBase64(cipher);
+			}
+		}
+	};
+
+	this.decrypt = function(ciphertext, key, ao){
+		var ip=dojo.crypto.outputTypes.Base64;
+		var mode=dojo.crypto.cipherModes.EBC;
+		if (ao){
+			if (ao.outputType) ip=ao.outputType;
+			if (ao.cipherMode) mode=ao.cipherMode;
+		}
+		var bx = init(key);
+		var pt=[];
+	
+		var c=null;
+		switch(ip){
+			case dojo.crypto.outputTypes.Hex:{
+				c=[];
+				var i=0;
+				while (i+1<ciphertext.length){
+					c.push(parseInt(ciphertext.substr(i,2),16));
+					i+=2;
+				}
+				break;
+			}
+			case dojo.crypto.outputTypes.String:{
+				c=[];
+				for (var i=0; i<ciphertext.length; i++){
+					c.push(ciphertext.charCodeAt(i));
+				}
+				break;
+			}
+			case dojo.crypto.outputTypes.Raw:{
+				c=ciphertext;	//	should be a byte array
+				break;
+			}
+			default:{
+				c=fromBase64(ciphertext);
+				break;
+			}
+		}
+
+		var count=c.length >> 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<count; i++){
+			o.left=c[pos]*POW24|c[pos+1]*POW16|c[pos+2]*POW8|c[pos+3];
+			o.right=c[pos+4]*POW24|c[pos+5]*POW16|c[pos+6]*POW8|c[pos+7];
+
+			if(isCBC){
+				var left=o.left;
+				var right=o.right;
+			}
+
+			db(o, bx);	//	decrypt the block
+
+			if(isCBC){
+				o.left=xor(o.left, vector.left);
+				o.right=xor(o.right, vector.right);
+				vector.left=left;
+				vector.right=right;
+			}
+
+			pt.push((o.left>>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<pt.length; i++)
+			pt[i]=String.fromCharCode(pt[i]);
+		return pt.join("");
+	};
+
+	this.setIV("0000000000000000", dojo.crypto.outputTypes.Hex);
+}();


Property changes on: trunk/PhotoOp/root/static/js/src/crypto/Blowfish.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/crypto/LICENSE
===================================================================
--- trunk/PhotoOp/root/static/js/src/crypto/LICENSE	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/crypto/LICENSE	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,11 @@
+License Disclaimer:
+
+All contents of this directory are Copyright (c) the Dojo Foundation, with the
+following exceptions:
+-------------------------------------------------------------------------------
+
+MD5.js, SHA1.js:
+	* Copyright 1998-2005, Paul Johnstone
+	  Distributed under the terms of the BSD License
+
+


Property changes on: trunk/PhotoOp/root/static/js/src/crypto/LICENSE
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/crypto/MD5.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/crypto/MD5.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/crypto/MD5.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,193 @@
+dojo.require("dojo.crypto");
+dojo.provide("dojo.crypto.MD5");
+
+/*	Return to a port of Paul Johnstone's MD5 implementation
+ *	http://pajhome.org.uk/crypt/md5/index.html
+ *
+ *	Copyright (C) Paul Johnston 1999 - 2002.
+ *	Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
+ * 	Distributed under the BSD License
+ *
+ *	Dojo port by Tom Trenka
+ *
+ *	2005-12-7
+ *	All conversions are internalized (no dependencies)
+ *	implemented getHMAC for message digest auth.
+ */
+dojo.crypto.MD5 = new function(){
+	var chrsz=8;
+	var mask=(1<<chrsz)-1;
+	function toWord(s) {
+	  var wa=[];
+	  for(var i=0; i<s.length*chrsz; i+=chrsz)
+		wa[i>>5]|=(s.charCodeAt(i/chrsz)&mask)<<(i%32);
+	  return wa;
+	}
+	function toString(wa){
+		var s=[];
+		for(var i=0; i<wa.length*32; i+=chrsz)
+			s.push(String.fromCharCode((wa[i>>5]>>>(i%32))&mask));
+		return s.join("");
+	}
+	function toHex(wa) {
+		var h="0123456789abcdef";
+		var s=[];
+		for(var i=0; i<wa.length*4; i++){
+			s.push(h.charAt((wa[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<wa.length*4; i+=3){
+			var t=(((wa[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<<c)|(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; i<x.length; i+=16){
+			var olda=a;
+			var oldb=b;
+			var oldc=c;
+			var oldd=d;
+
+			a=FF(a,b,c,d,x[i+ 0],7 ,-680876936);
+			d=FF(d,a,b,c,x[i+ 1],12,-389564586);
+			c=FF(c,d,a,b,x[i+ 2],17, 606105819);
+			b=FF(b,c,d,a,x[i+ 3],22,-1044525330);
+			a=FF(a,b,c,d,x[i+ 4],7 ,-176418897);
+			d=FF(d,a,b,c,x[i+ 5],12, 1200080426);
+			c=FF(c,d,a,b,x[i+ 6],17,-1473231341);
+			b=FF(b,c,d,a,x[i+ 7],22,-45705983);
+			a=FF(a,b,c,d,x[i+ 8],7 , 1770035416);
+			d=FF(d,a,b,c,x[i+ 9],12,-1958414417);
+			c=FF(c,d,a,b,x[i+10],17,-42063);
+			b=FF(b,c,d,a,x[i+11],22,-1990404162);
+			a=FF(a,b,c,d,x[i+12],7 , 1804603682);
+			d=FF(d,a,b,c,x[i+13],12,-40341101);
+			c=FF(c,d,a,b,x[i+14],17,-1502002290);
+			b=FF(b,c,d,a,x[i+15],22, 1236535329);
+
+			a=GG(a,b,c,d,x[i+ 1],5 ,-165796510);
+			d=GG(d,a,b,c,x[i+ 6],9 ,-1069501632);
+			c=GG(c,d,a,b,x[i+11],14, 643717713);
+			b=GG(b,c,d,a,x[i+ 0],20,-373897302);
+			a=GG(a,b,c,d,x[i+ 5],5 ,-701558691);
+			d=GG(d,a,b,c,x[i+10],9 , 38016083);
+			c=GG(c,d,a,b,x[i+15],14,-660478335);
+			b=GG(b,c,d,a,x[i+ 4],20,-405537848);
+			a=GG(a,b,c,d,x[i+ 9],5 , 568446438);
+			d=GG(d,a,b,c,x[i+14],9 ,-1019803690);
+			c=GG(c,d,a,b,x[i+ 3],14,-187363961);
+			b=GG(b,c,d,a,x[i+ 8],20, 1163531501);
+			a=GG(a,b,c,d,x[i+13],5 ,-1444681467);
+			d=GG(d,a,b,c,x[i+ 2],9 ,-51403784);
+			c=GG(c,d,a,b,x[i+ 7],14, 1735328473);
+			b=GG(b,c,d,a,x[i+12],20,-1926607734);
+
+			a=HH(a,b,c,d,x[i+ 5],4 ,-378558);
+			d=HH(d,a,b,c,x[i+ 8],11,-2022574463);
+			c=HH(c,d,a,b,x[i+11],16, 1839030562);
+			b=HH(b,c,d,a,x[i+14],23,-35309556);
+			a=HH(a,b,c,d,x[i+ 1],4 ,-1530992060);
+			d=HH(d,a,b,c,x[i+ 4],11, 1272893353);
+			c=HH(c,d,a,b,x[i+ 7],16,-155497632);
+			b=HH(b,c,d,a,x[i+10],23,-1094730640);
+			a=HH(a,b,c,d,x[i+13],4 , 681279174);
+			d=HH(d,a,b,c,x[i+ 0],11,-358537222);
+			c=HH(c,d,a,b,x[i+ 3],16,-722521979);
+			b=HH(b,c,d,a,x[i+ 6],23, 76029189);
+			a=HH(a,b,c,d,x[i+ 9],4 ,-640364487);
+			d=HH(d,a,b,c,x[i+12],11,-421815835);
+			c=HH(c,d,a,b,x[i+15],16, 530742520);
+			b=HH(b,c,d,a,x[i+ 2],23,-995338651);
+
+			a=II(a,b,c,d,x[i+ 0],6 ,-198630844);
+			d=II(d,a,b,c,x[i+ 7],10, 1126891415);
+			c=II(c,d,a,b,x[i+14],15,-1416354905);
+			b=II(b,c,d,a,x[i+ 5],21,-57434055);
+			a=II(a,b,c,d,x[i+12],6 , 1700485571);
+			d=II(d,a,b,c,x[i+ 3],10,-1894986606);
+			c=II(c,d,a,b,x[i+10],15,-1051523);
+			b=II(b,c,d,a,x[i+ 1],21,-2054922799);
+			a=II(a,b,c,d,x[i+ 8],6 , 1873313359);
+			d=II(d,a,b,c,x[i+15],10,-30611744);
+			c=II(c,d,a,b,x[i+ 6],15,-1560198380);
+			b=II(b,c,d,a,x[i+13],21, 1309151649);
+			a=II(a,b,c,d,x[i+ 4],6 ,-145523070);
+			d=II(d,a,b,c,x[i+11],10,-1120210379);
+			c=II(c,d,a,b,x[i+ 2],15, 718787259);
+			b=II(b,c,d,a,x[i+ 9],21,-343485551);
+
+			a = add(a,olda);
+			b = add(b,oldb);
+			c = add(c,oldc);
+			d = add(d,oldd);
+		}
+		return [a,b,c,d];
+	}
+	function hmac(data,key){
+		var wa=toWord(key);
+		if(wa.length>16) 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));
+			}
+		}
+	};
+}();


Property changes on: trunk/PhotoOp/root/static/js/src/crypto/MD5.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/crypto/Rijndael.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/crypto/Rijndael.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/crypto/Rijndael.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,22 @@
+/*
+	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.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){
+	};
+}();


Property changes on: trunk/PhotoOp/root/static/js/src/crypto/Rijndael.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/crypto/SHA1.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/crypto/SHA1.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/crypto/SHA1.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -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<<chrsz)-1;
+	function toWord(s) {
+	  var wa=[];
+	  for(var i=0; i<s.length*chrsz; i+=chrsz)
+		wa[i>>5]|=(s.charCodeAt(i/chrsz)&mask)<<(i%32);
+	  return wa;
+	}
+	function toString(wa){
+		var s=[];
+		for(var i=0; i<wa.length*32; i+=chrsz)
+			s.push(String.fromCharCode((wa[i>>5]>>>(i%32))&mask));
+		return s.join("");
+	}
+	function toHex(wa) {
+		var h="0123456789abcdef";
+		var s=[];
+		for(var i=0; i<wa.length*4; i++){
+			s.push(h.charAt((wa[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<wa.length*4; i+=3){
+			var t=(((wa[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<<n)|(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; i<x.length; i+=16){
+			var olda=a;
+			var oldb=b;
+			var oldc=c;
+			var oldd=d;
+			var olde=e;
+
+			for(var j=0; j<80; j++){
+				if(j<16) w[j]=x[i+j];
+				else w[j]=r(w[j-3]^w[j-8]^w[j-14]^w[j-16],1);
+				var t=add(add(r(a,5),fn(j,b,c,d)),add(add(e,w[j]),cnst(j)));
+				e=d; d=c; c=r(b,30); b=a; a=t;
+			}
+
+			a=add(a,olda);
+			b=add(b,oldb);
+			c=add(c,oldc);
+			d=add(d,oldd);
+			e=add(e,olde);
+		}
+		return [a,b,c,d,e];
+	}
+	function hmac(data,key){
+		var wa=toWord(key);
+		if(wa.length>16) 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));
+			}
+		}
+	};
+}();


Property changes on: trunk/PhotoOp/root/static/js/src/crypto/SHA1.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/crypto/SHA256.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/crypto/SHA256.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/crypto/SHA256.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,20 @@
+/*
+	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.crypto.SHA256");
+dojo.require("dojo.crypto");
+dojo.require("dojo.experimental");
+
+dojo.experimental("dojo.crypto.SHA256");
+
+dojo.crypto.SHA256 = new function(){
+	this.compute=function(s){
+	};
+}();


Property changes on: trunk/PhotoOp/root/static/js/src/crypto/SHA256.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/crypto/__package__.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/crypto/__package__.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/crypto/__package__.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,17 @@
+/*
+	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.kwCompoundRequire({
+	common: [
+		"dojo.crypto",
+		"dojo.crypto.MD5"
+	]
+});
+dojo.provide("dojo.crypto.*");


Property changes on: trunk/PhotoOp/root/static/js/src/crypto/__package__.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/crypto.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/crypto.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/crypto.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -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.provide("dojo.crypto");
+
+//	enumerations for use in crypto code. Note that 0 == default, for the most part.
+dojo.crypto.cipherModes={ ECB:0, CBC:1, PCBC:2, CFB:3, OFB:4, CTR:5 };
+dojo.crypto.outputTypes={ Base64:0,Hex:1,String:2,Raw:3 };


Property changes on: trunk/PhotoOp/root/static/js/src/crypto.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/data/Attribute.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/data/Attribute.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/data/Attribute.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,62 @@
+/*
+	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.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"]);
+	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);
+};


Property changes on: trunk/PhotoOp/root/static/js/src/data/Attribute.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/data/Item.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/data/Item.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/data/Item.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,332 @@
+/*
+	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.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"]);
+	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:
+	 * <pre>
+	 *   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);
+	 * </pre>
+	 */
+	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]);
+	for (var key in this._dictionaryOfAttributeValues) {
+		if (key == attributeId) {
+			return true; // boolean
+		}
+	}
+	return false; // 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
+};
+
+


Property changes on: trunk/PhotoOp/root/static/js/src/data/Item.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/data/Kind.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/data/Kind.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/data/Kind.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,28 @@
+/*
+	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.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);


Property changes on: trunk/PhotoOp/root/static/js/src/data/Kind.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/data/Observable.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/data/Observable.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/data/Observable.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -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.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
+};
+


Property changes on: trunk/PhotoOp/root/static/js/src/data/Observable.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/data/ResultSet.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/data/ResultSet.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/data/ResultSet.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,70 @@
+/*
+	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.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"]);
+	dojo.lang.assertType(arrayOfItems, [Array, "optional"]);
+	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


Property changes on: trunk/PhotoOp/root/static/js/src/data/ResultSet.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/data/Type.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/data/Type.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/data/Type.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,25 @@
+/*
+	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.data.Type");
+dojo.require("dojo.data.Item");
+
+// -------------------------------------------------------------------
+// Constructor
+// -------------------------------------------------------------------
+dojo.data.Type = function(/* dojo.data.provider.Base */ dataProvider) {
+	/**
+	 * summary:
+	 * A Type represents a type of value, like Text, Number, Picture,
+	 * or Varchar.
+	 */
+	dojo.data.Item.call(this, dataProvider);
+};
+dojo.inherits(dojo.data.Type, dojo.data.Item);


Property changes on: trunk/PhotoOp/root/static/js/src/data/Type.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/data/Value.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/data/Value.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/data/Value.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,55 @@
+/*
+	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.data.Value");
+dojo.require("dojo.lang.assert");
+
+// -------------------------------------------------------------------
+// Constructor
+// -------------------------------------------------------------------
+dojo.data.Value = function(/* anything */ value) {
+	/**
+	 * summary:
+	 * A Value represents a simple literal value (like "foo" or 334),
+	 * or a reference value (a pointer to an Item).
+	 */
+	this._value = value;
+	this._type = null;
+};
+
+// -------------------------------------------------------------------
+// Public instance methods
+// -------------------------------------------------------------------
+dojo.data.Value.prototype.toString = function() {
+	return this._value.toString(); // string
+};
+
+dojo.data.Value.prototype.getValue = function() {
+	/**
+	 * summary: Returns the value itself.
+	 */ 
+	return this._value; // anything
+};
+
+dojo.data.Value.prototype.getType = function() {
+	/**
+	 * summary: Returns the data type of the value.
+	 */ 
+	dojo.unimplemented('dojo.data.Value.prototype.getType');
+	return this._type; // dojo.data.Type
+};
+
+dojo.data.Value.prototype.compare = function() {
+	dojo.unimplemented('dojo.data.Value.prototype.compare');
+};
+
+dojo.data.Value.prototype.isEqual = function() {
+	dojo.unimplemented('dojo.data.Value.prototype.isEqual');
+};


Property changes on: trunk/PhotoOp/root/static/js/src/data/Value.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/data/__package__.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/data/__package__.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/data/__package__.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,22 @@
+/*
+	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.experimental");
+
+dojo.experimental("dojo.data.*");
+dojo.kwCompoundRequire({
+	common: [
+		"dojo.data.Item",
+		"dojo.data.ResultSet",
+		"dojo.data.provider.FlatFile"
+	]
+});
+dojo.provide("dojo.data.*");
+


Property changes on: trunk/PhotoOp/root/static/js/src/data/__package__.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/data/format/Csv.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/data/format/Csv.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/data/format/Csv.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,112 @@
+/*
+	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.data.format.Csv");
+dojo.require("dojo.lang.assert");
+
+
+dojo.data.format.Csv = new function() {
+
+	// -------------------------------------------------------------------
+	// Public functions
+	// -------------------------------------------------------------------
+	this.getArrayStructureFromCsvFileContents = function(/* string */ csvFileContents) {
+		/**
+		 * Given a string containing CSV records, this method parses
+		 * the string and returns a data structure containing the parsed
+		 * content.  The data structure we return is an array of length
+		 * R, where R is the number of rows (lines) in the CSV data.  The 
+		 * return array contains one sub-array for each CSV line, and each 
+		 * sub-array contains C string values, where C is the number of 
+		 * columns in the CSV data.
+		 * 
+		 * For example, given this CSV string as input:
+		 * <pre>
+		 *   "Title, Year, Producer \n Alien, 1979, Ridley Scott \n Blade Runner, 1982, Ridley Scott"
+		 * </pre>
+		 * We will return this data structure:
+		 * <pre>
+		 *   [["Title", "Year", "Producer"]
+		 *    ["Alien", "1979", "Ridley Scott"],  
+		 *    ["Blade Runner", "1982", "Ridley Scott"]]
+		 * </pre>
+		 */
+		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
+	};
+	
+}();


Property changes on: trunk/PhotoOp/root/static/js/src/data/format/Csv.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/data/format/Json.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/data/format/Json.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/data/format/Json.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,103 @@
+/*
+	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.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"]);
+		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);
+				}
+			}
+		}
+	}
+	
+}();
+


Property changes on: trunk/PhotoOp/root/static/js/src/data/format/Json.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/data/provider/Base.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/data/provider/Base.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/data/provider/Base.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,183 @@
+/*
+	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.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"]);
+	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); // FIXME: should be optional
+	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
+};
+


Property changes on: trunk/PhotoOp/root/static/js/src/data/provider/Base.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/data/provider/Delicious.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/data/provider/Delicious.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/data/provider/Delicious.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,85 @@
+/*
+	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.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:
+	 * <script type="text/javascript" src="http://del.icio.us/feeds/json/gumption?count=8"></script>
+	 */
+	dojo.data.provider.FlatFile.call(this);
+	// Delicious = null;
+	if (Delicious && Delicious.posts) {
+		dojo.data.format.Json.loadDataProviderFromArrayOfJsonData(this, Delicious.posts);
+	} else {
+		// document.write("<script type='text/javascript'>dojo.data.provider.Delicious._fetchComplete()</script>");		
+		/*
+		document.write("<script type='text/javascript'>alert('boo!');</script>");		
+		document.write("<script type='text/javascript'>var foo = 'not dojo'; alert('dojo == ' + foo);</script>");		
+		document.write("<script type='text/javascript'>var foo = fetchComplete; alert('dojo == ' + foo);</script>");		
+		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("<script type='text/javascript' src='" + sourceUrl + "'></script>");
+		document.write("<script type='text/javascript'>alert('line 63: ' + Delicious.posts[0].u);</script>");		
+		document.write("<script type='text/javascript'>callMe();</script>");		
+	alert("line 66");
+	dojo.data.format.Json.loadDataProviderFromArrayOfJsonData(this, Delicious.posts);
+	return this._currentArray; // Array
+};
+
+callMe = function() {
+	alert("callMe!");
+};
+
+*/


Property changes on: trunk/PhotoOp/root/static/js/src/data/provider/Delicious.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/data/provider/FlatFile.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/data/provider/FlatFile.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/data/provider/FlatFile.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,153 @@
+/*
+	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.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"]);
+	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"]);
+	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
+};
+


Property changes on: trunk/PhotoOp/root/static/js/src/data/provider/FlatFile.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/data/provider/JotSpot.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/data/provider/JotSpot.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/data/provider/JotSpot.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,27 @@
+/*
+	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.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);
+


Property changes on: trunk/PhotoOp/root/static/js/src/data/provider/JotSpot.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/data/provider/MySql.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/data/provider/MySql.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/data/provider/MySql.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,27 @@
+/*
+	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.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);


Property changes on: trunk/PhotoOp/root/static/js/src/data/provider/MySql.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/data/to_do.txt
===================================================================
--- trunk/PhotoOp/root/static/js/src/data/to_do.txt	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/data/to_do.txt	2009-02-07 13:12:54 UTC (rev 5416)
@@ -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 <table></table>
+ * 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
+


Property changes on: trunk/PhotoOp/root/static/js/src/data/to_do.txt
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/data.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/data.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/data.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -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.provide("dojo.data");
+
+// currently a stub for dojo.data
+
+dojo.data = {};


Property changes on: trunk/PhotoOp/root/static/js/src/data.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/date.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/date.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/date.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,761 @@
+/*
+	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.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);
+	
+	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 <http://www.opengroup.org/onlinepubs/007908799/xsh/strftime.html>
+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 "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()<d2.valueOf()){
+		return -1;	//	int
+	}
+	return 0;	//	int
+}
+
+dojo.date.dateParts={ 
+	//	summary
+	//	constants for use in dojo.date.add
+	YEAR:0, MONTH:1, DAY:2, HOUR:3, MINUTE:4, SECOND:5, MILLISECOND:6 
+};
+dojo.date.add=function(/* Date */ d, /* dojo.date.dateParts */ unit, /* int */ amount){
+	var n=(amount)?amount:1;
+	var v;
+	switch(unit){
+		case dojo.date.dateParts.YEAR:{
+			v=new Date(d.getFullYear()+n, d.getMonth(), d.getDate(), d.getHours(), d.getMinutes(), d.getSeconds(), d.getMilliseconds());
+			break;
+		}
+		case dojo.date.dateParts.MONTH:{
+			v=new Date(d.getFullYear(), d.getMonth()+n, d.getDate(), d.getHours(), d.getMinutes(), d.getSeconds(), d.getMilliseconds());
+			break;
+		}
+		case dojo.date.dateParts.HOUR:{
+			v=new Date(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours()+n, d.getMinutes(), d.getSeconds(), d.getMilliseconds());
+			break;
+		}
+		case dojo.date.dateParts.MINUTE:{
+			v=new Date(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours(), d.getMinutes()+n, d.getSeconds(), d.getMilliseconds());
+			break;
+		}
+		case dojo.date.dateParts.SECOND:{
+			v=new Date(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours(), d.getMinutes(), d.getSeconds()+n, d.getMilliseconds());
+			break;
+		}
+		case dojo.date.dateParts.MILLISECOND:{
+			v=new Date(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours(), d.getMinutes(), d.getSeconds(), d.getMilliseconds()+n);
+			break;
+		}
+		default:{
+			v=new Date(d.getFullYear(), d.getMonth(), d.getDate()+n, d.getHours(), d.getMinutes(), d.getSeconds(), d.getMilliseconds());
+		}
+	};
+	return v;	//	Date
+};
+
+/* Deprecated
+ *************/
+
+
+dojo.date.toString = function(date, format){
+	dojo.deprecated("dojo.date.toString",
+		"use dojo.date.format instead", "0.4");
+
+	if (format.indexOf("#d") > -1) {
+		format = format.replace(/#dddd/g, dojo.date.getDayOfWeekName(date));
+		format = format.replace(/#ddd/g, dojo.date.getShortDayOfWeekName(date));
+		format = format.replace(/#dd/g, (date.getDate().toString().length==1?"0":"")+date.getDate());
+		format = format.replace(/#d/g, date.getDate());
+	}
+
+	if (format.indexOf("#M") > -1) {
+		format = format.replace(/#MMMM/g, dojo.date.getMonthName(date));
+		format = format.replace(/#MMM/g, dojo.date.getShortMonthName(date));
+		format = format.replace(/#MM/g, ((date.getMonth()+1).toString().length==1?"0":"")+(date.getMonth()+1));
+		format = format.replace(/#M/g, date.getMonth() + 1);
+	}
+
+	if (format.indexOf("#y") > -1) {
+		var fullYear = date.getFullYear().toString();
+		format = format.replace(/#yyyy/g, fullYear);
+		format = format.replace(/#yy/g, fullYear.substring(2));
+		format = format.replace(/#y/g, fullYear.substring(3));
+	}
+
+	// Return if only date needed;
+	if (format.indexOf("#") == -1) {
+		return format;
+	}
+	
+	if (format.indexOf("#h") > -1) {
+		var hours = date.getHours();
+		hours = (hours > 12 ? hours - 12 : (hours == 0) ? 12 : hours);
+		format = format.replace(/#hh/g, (hours.toString().length==1?"0":"")+hours);
+		format = format.replace(/#h/g, hours);
+	}
+	
+	if (format.indexOf("#H") > -1) {
+		format = format.replace(/#HH/g, (date.getHours().toString().length==1?"0":"")+date.getHours());
+		format = format.replace(/#H/g, date.getHours());
+	}
+	
+	if (format.indexOf("#m") > -1) {
+		format = format.replace(/#mm/g, (date.getMinutes().toString().length==1?"0":"")+date.getMinutes());
+		format = format.replace(/#m/g, date.getMinutes());
+	}
+
+	if (format.indexOf("#s") > -1) {
+		format = format.replace(/#ss/g, (date.getSeconds().toString().length==1?"0":"")+date.getSeconds());
+		format = format.replace(/#s/g, date.getSeconds());
+	}
+	
+	if (format.indexOf("#T") > -1) {
+		format = format.replace(/#TT/g, date.getHours() >= 12 ? "PM" : "AM");
+		format = format.replace(/#T/g, date.getHours() >= 12 ? "P" : "A");
+	}
+
+	if (format.indexOf("#t") > -1) {
+		format = format.replace(/#tt/g, date.getHours() >= 12 ? "pm" : "am");
+		format = format.replace(/#t/g, date.getHours() >= 12 ? "p" : "a");
+	}
+					
+	return format;
+	
+}
+
+
+dojo.date.daysInMonth = function (month, year) {
+	dojo.deprecated("daysInMonth(month, year)",
+		"replaced by getDaysInMonth(dateObject)", "0.4");
+	return dojo.date.getDaysInMonth(new Date(year, month, 1));
+}
+
+/**
+ *
+ * Returns a string of the date in the version "January 1, 2004"
+ *
+ * @param date The date object
+ */
+dojo.date.toLongDateString = function(date) {
+	dojo.deprecated("dojo.date.toLongDateString",
+		'use dojo.date.format(date, "%B %e, %Y") instead', "0.4");
+	return dojo.date.format(date, "%B %e, %Y")
+}
+
+/**
+ *
+ * Returns a string of the date in the version "Jan 1, 2004"
+ *
+ * @param date The date object
+ */
+dojo.date.toShortDateString = function(date) {
+	dojo.deprecated("dojo.date.toShortDateString",
+		'use dojo.date.format(date, "%b %e, %Y") instead', "0.4");
+	return dojo.date.format(date, "%b %e, %Y");
+}
+
+/**
+ *
+ * Returns military formatted time
+ *
+ * @param date the date object
+ */
+dojo.date.toMilitaryTimeString = function(date){
+	dojo.deprecated("dojo.date.toMilitaryTimeString",
+		'use dojo.date.format(date, "%T")', "0.4");
+	return dojo.date.format(date, "%T");
+}
+
+/**
+ *
+ * Returns a string of the date relative to the current date.
+ *
+ * @param date The date object
+ *
+ * Example returns:
+ * - "1 minute ago"
+ * - "4 minutes ago"
+ * - "Yesterday"
+ * - "2 days ago"
+ */
+dojo.date.toRelativeString = function(date) {
+	var now = new Date();
+	var diff = (now - date) / 1000;
+	var end = " ago";
+	var future = false;
+	if(diff < 0) {
+		future = true;
+		end = " from now";
+		diff = -diff;
+	}
+
+	if(diff < 60) {
+		diff = Math.round(diff);
+		return diff + " second" + (diff == 1 ? "" : "s") + end;
+	} else if(diff < 3600) {
+		diff = Math.round(diff/60);
+		return diff + " minute" + (diff == 1 ? "" : "s") + end;
+	} else if(diff < 3600*24 && date.getDay() == now.getDay()) {
+		diff = Math.round(diff/3600);
+		return diff + " hour" + (diff == 1 ? "" : "s") + end;
+	} else if(diff < 3600*24*7) {
+		diff = Math.round(diff/(3600*24));
+		if(diff == 1) {
+			return future ? "Tomorrow" : "Yesterday";
+		} else {
+			return diff + " days" + end;
+		}
+	} else {
+		return dojo.date.toShortDateString(date);
+	}
+}
+
+/**
+ * Retrieves the day of the week the Date is set to.
+ *
+ * @return The day of the week
+ */
+dojo.date.getDayOfWeekName = function (date) {
+	dojo.deprecated("dojo.date.getDayOfWeekName",
+		"use dojo.date.getDayName instead", "0.4");
+	return dojo.date.days[date.getDay()];
+}
+
+/**
+ * Retrieves the short day of the week name the Date is set to.
+ *
+ * @return The short day of the week name
+ */
+dojo.date.getShortDayOfWeekName = function (date) {
+	dojo.deprecated("dojo.date.getShortDayOfWeekName",
+		"use dojo.date.getDayShortName instead", "0.4");
+	return dojo.date.shortDays[date.getDay()];
+}
+
+/**
+ * Retrieves the short month name the Date is set to.
+ *
+ * @return The short month name
+ */
+dojo.date.getShortMonthName = function (date) {
+	dojo.deprecated("dojo.date.getShortMonthName",
+		"use dojo.date.getMonthShortName instead", "0.4");
+	return dojo.date.shortMonths[date.getMonth()];
+}
+
+
+/**
+ * Convert a Date to a SQL string, optionally ignoring the HH:MM:SS portion of the Date
+ */
+dojo.date.toSql = function(date, noTime) {
+	return dojo.date.format(date, "%F" + !noTime ? " %T" : "");
+}
+
+/**
+ * Convert a SQL date string to a JavaScript Date object
+ */
+dojo.date.fromSql = function(sqlDate) {
+	var parts = sqlDate.split(/[\- :]/g);
+	while(parts.length < 6) {
+		parts.push(0);
+	}
+	return new Date(parts[0], (parseInt(parts[1],10)-1), parts[2], parts[3], parts[4], parts[5]);
+}
+


Property changes on: trunk/PhotoOp/root/static/js/src/date.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/debug/Firebug.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/debug/Firebug.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/debug/Firebug.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,17 @@
+/*
+	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.debug.Firebug");
+
+if (console.log) {
+	dojo.hostenv.println=console.log;
+} else {
+	dojo.debug("dojo.debug.Firebug requires Firebug > 0.4");
+}


Property changes on: trunk/PhotoOp/root/static/js/src/debug/Firebug.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/debug/arrow_hide.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/debug/arrow_hide.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/debug/arrow_show.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/debug/arrow_show.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/debug/deep.html
===================================================================
--- trunk/PhotoOp/root/static/js/src/debug/deep.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/debug/deep.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,359 @@
+<html>
+<head>
+<title>Deep Debugger</title>
+<script>
+
+var tableRows = {};
+var tableCels = {};
+var tableObjs = {};
+var tablesBuilt = {};
+var tableShows = {};
+var tableHides = {};
+
+// IE: nodes w/id need to be redeclared or getElementById is b0rked
+var frame = null;
+
+window.onload = function(){
+	// if IE loads this page too quickly (instantly) then 
+	// window.debugVar might not have been set
+	window.setTimeout(startMeUp, 100);
+}
+
+function startMeUp(){
+	frame = document.getElementById('frame');
+	buildTable('root', frame, window.debugVar);
+}
+
+function buildTable(path, parent, obj){
+
+	var keys = [];
+	var vals = [];
+	for(var prop in obj){
+		keys.push(prop);
+		try {
+			vals[prop] = obj[prop];
+		} catch(E) {
+			vals[prop] = 'ERROR: ' + E.message;
+		}
+	}
+	keys.sort(keySorter);
+
+	if (!keys.length){
+
+		var div = document.createElement('div');
+		div.appendChild(document.createTextNode('Object has no properties.'));
+
+		parent.appendChild(div);
+		return;
+	}
+
+
+	var t = document.createElement('table');
+	t.border = "1";
+
+	var tb = document.createElement('tbody');
+	t.appendChild(tb);
+
+
+	for(var i = 0; i < keys.length; i++) {
+		buildTableRow(path+'-'+keys[i], tb, keys[i], vals[keys[i]]);
+	}
+
+	if (path == 'root'){
+		//t.style.width = '90%';
+	}
+	t.style.width = '100%';
+
+	parent.appendChild(t);
+
+	tablesBuilt[path] = true;
+}
+
+function buildTableRow(path, tb, name, value) {
+
+	var simpleType = typeof(value);
+	var createSubrow = (simpleType == 'object');
+	var complexType = simpleType;
+
+	if (simpleType == 'object'){
+		var cls = getConstructorClass(value);
+		if (cls){
+			if (cls == 'Object'){
+			}else if (cls == 'Array'){
+				complexType = 'array';
+			}else{
+				complexType += ' ('+cls+')';
+			}
+		}
+	}
+
+/*var tr1 = document.createElement('tr');
+	var td1 = document.createElement('td');
+	var td2 = document.createElement('td');
+	var td3 = document.createElement('td');
+	var td4 = document.createElement('td');*/
+
+	var row = tb.rows.length;
+	var tr1 = tb.insertRow(row++);
+	var td1 = tr1.insertCell(0);
+	var td2 = tr1.insertCell(1);
+	var td3 = tr1.insertCell(2);
+	var td4 = tr1.insertCell(3);
+	
+	tr1.style.verticalAlign = 'top';
+	td1.style.verticalAlign = 'middle';
+
+	td1.className = 'propPlus';
+	td2.className = 'propName';
+	td3.className = 'propType';
+	td4.className = 'propVal';
+
+	//tr1.appendChild(td1);
+	//tr1.appendChild(td2);
+	//tr1.appendChild(td3);
+	//tr1.appendChild(td4);
+
+	if (createSubrow){
+		var img1 = document.createElement('img');
+		img1.width = 9;
+		img1.height = 9;
+		img1.src = 'arrow_show.gif';
+		var a1 = document.createElement('a');
+		a1.appendChild(img1);
+		a1.href = '#';
+		a1.onclick = function(){ showTableRow(path); return false; };
+
+		var img2 = document.createElement('img');
+		img2.width = 9;
+		img2.height = 9;
+		img2.src = 'arrow_hide.gif';
+		var a2 = document.createElement('a');
+		a2.appendChild(img2);
+		a2.href = '#';
+		a2.onclick = function(){ hideTableRow(path); return false; };
+		a2.style.display = 'none';
+
+		tableShows[path] = a1;
+		tableHides[path] = a2;
+
+		td1.appendChild(a1);
+		td1.appendChild(a2);
+	}else{
+		var img = document.createElement('img');
+		img.width = 9;
+		img.height = 9;
+		img.src = 'spacer.gif';
+
+		td1.appendChild(img);
+	}
+
+	td2.appendChild(document.createTextNode(name));
+	td3.appendChild(document.createTextNode(complexType));
+	td4.appendChild(buildPreBlock(value));
+
+	//tb.appendChild(tr1);
+
+	if (createSubrow){
+		var tr2 = tb.insertRow(row++);
+		var td5 = tr2.insertCell(0);
+		var td6 = tr2.insertCell(1);
+		
+		//var tr2 = document.createElement('tr');
+		//var td5 = document.createElement('td');
+		//var td6 = document.createElement('td');
+
+		td5.innerHTML = '&nbsp;';
+		//td6.innerHTML = '&nbsp;';
+
+		td6.colSpan = '3';
+
+		tr2.appendChild(td5);
+		tr2.appendChild(td6);
+
+		tr2.style.display = 'none';
+
+		tb.appendChild(tr2);
+
+		tableRows[path] = tr2;
+		tableCels[path] = td6;
+		tableObjs[path] = value;
+	}
+}
+
+function showTableRow(path){
+
+	var tr = tableRows[path];
+	var td = tableCels[path];
+	var a1 = tableShows[path];
+	var a2 = tableHides[path];
+
+	if (!tablesBuilt[path]){
+
+		//alert('building table for '+path);
+		buildTable(path, td, tableObjs[path]);
+	}
+
+	tr.style.display = 'table-row';
+
+	a1.style.display = 'none';
+	a2.style.display = 'inline';
+}
+
+function hideTableRow(path){
+
+	var tr = tableRows[path];
+	var a1 = tableShows[path];
+	var a2 = tableHides[path];
+
+	tr.style.display = 'none';
+
+	a1.style.display = 'inline';
+	a2.style.display = 'none';
+}
+
+function buildPreBlock(value){
+
+	//
+	// how many lines ?
+	//
+
+	var s = ''+value;
+	s = s.replace("\r\n", "\n");
+	s = s.replace("\r", "");
+	var lines = s.split("\n");
+
+
+	if (lines.length < 2){
+
+		if (lines[0].length < 60){
+
+			var pre = document.createElement('pre');
+			pre.appendChild(document.createTextNode(s));
+			return pre;
+		}
+	}
+
+
+	//
+	// multiple lines :(
+	//
+
+	var preview = lines[0].substr(0, 60) + ' ...';
+
+	var pre1 = document.createElement('pre');
+	pre1.appendChild(document.createTextNode(preview));
+	pre1.className = 'clicky';
+
+	var pre2 = document.createElement('pre');
+	pre2.appendChild(document.createTextNode(s));
+	pre2.style.display = 'none';
+	pre2.className = 'clicky';
+
+	pre1.onclick = function(){
+		pre1.style.display = 'none';
+		pre2.style.display = 'block';
+	}
+
+	pre2.onclick = function(){
+		pre1.style.display = 'block';
+		pre2.style.display = 'none';
+	}
+
+	var pre = document.createElement('div');
+
+	pre.appendChild(pre1);
+	pre.appendChild(pre2);
+
+	return pre;
+}
+
+function getConstructorClass(obj){
+
+	if (!obj.constructor || !obj.constructor.toString) return;
+
+	var m = obj.constructor.toString().match(/function\s*(\w+)/);
+
+	if (m && m.length == 2) return m[1];
+
+	return null;
+}
+
+function keySorter(a, b){
+
+	if (a == parseInt(a) && b == parseInt(b)){
+
+		return (parseInt(a) > parseInt(b)) ? 1 : ((parseInt(a) < parseInt(b)) ? -1 : 0);
+	}
+
+	// sort by lowercase string
+
+	var a2 = String(a).toLowerCase();
+	var b2 = String(b).toLowerCase();
+
+	return (a2 > b2) ? 1 : ((a2 < b2) ? -1 : 0);
+}
+
+</script>
+<style>
+
+body {
+	font-family: arial, helvetica, sans-serif;
+}
+
+table {
+	border-width: 0px;
+	border-spacing: 1px;
+	border-collapse: separate;
+}
+
+td {
+	border-width: 0px;
+	padding: 2px;
+}
+
+img {
+	border: 0;
+}
+
+pre {
+	margin: 0;
+	padding: 0;
+	white-space: -moz-pre-wrap;  /* Mozilla, supported since 1999 */
+	white-space: -pre-wrap;      /* Opera 4 - 6 */
+	white-space: -o-pre-wrap;    /* Opera 7 */
+	white-space: pre-wrap;       /* CSS3 - Text module (Candidate Recommendation) http://www.w3.org/TR/css3-text/#white-space */
+	word-wrap: break-word;       /* IE 5.5+ */
+}
+
+pre.clicky {
+	cursor: hand;
+	cursor: pointer;
+}
+
+td.propPlus {
+	width: 9px;
+	background-color: #ddd;
+}
+
+td.propName {
+	background-color: #ddd;
+}
+
+td.propType {
+	background-color: #ddd;
+}
+
+td.propVal {
+	background-color: #ddd;
+}
+
+</style>
+</head>
+<body>
+
+<h2>Javascript Object Browser</h2>
+
+<div id="frame"></div>
+
+</body>
+</html>
\ No newline at end of file


Property changes on: trunk/PhotoOp/root/static/js/src/debug/deep.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/debug/spacer.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/debug/spacer.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/debug.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/debug.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/debug.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,80 @@
+/*
+	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
+*/
+
+/**
+ * Produce a line of debug output. 
+ * Does nothing unless djConfig.isDebug is true.
+ * varargs, joined with ''.
+ * Caller should not supply a trailing "\n".
+ */
+dojo.debug = function(){
+	if (!djConfig.isDebug) { return; }
+	var args = arguments;
+	if(dj_undef("println", dojo.hostenv)){
+		dojo.raise("dojo.debug not available (yet?)");
+	}
+	var isJUM = dj_global["jum"] && !dj_global["jum"].isBrowser;
+	var s = [(isJUM ? "": "DEBUG: ")];
+	for(var i=0;i<args.length;++i){
+		if(!false && args[i] && args[i] instanceof Error){
+			var msg = "[" + args[i].name + ": " + dojo.errorToString(args[i]) +
+				(args[i].fileName ? ", file: " + args[i].fileName : "") +
+				(args[i].lineNumber ? ", line: " + args[i].lineNumber : "") + "]";
+		} else {
+			try {
+				var msg = String(args[i]);
+			} catch(e) {
+				if(dojo.render.html.ie) {
+					var msg = "[ActiveXObject]";
+				} else {
+					var msg = "[unknown]";
+				}
+			}
+		}
+		s.push(msg);
+	}
+	if(isJUM){ // this seems to be the only way to get JUM to "play nice"
+		jum.debug(s.join(" "));
+	}else{
+		dojo.hostenv.println(s.join(" "));
+	}
+}
+
+/**
+ * this is really hacky for now - just 
+ * display the properties of the object
+**/
+
+dojo.debugShallow = function(obj){
+	if (!djConfig.isDebug) { return; }
+	dojo.debug('------------------------------------------------------------');
+	dojo.debug('Object: '+obj);
+	var props = [];
+	for(var prop in obj){
+		try {
+			props.push(prop + ': ' + obj[prop]);
+		} catch(E) {
+			props.push(prop + ': ERROR - ' + E.message);
+		}
+	}
+	props.sort();
+	for(var i = 0; i < props.length; i++) {
+		dojo.debug(props[i]);
+	}
+	dojo.debug('------------------------------------------------------------');
+}
+
+dojo.debugDeep = function(obj){
+	if (!djConfig.isDebug) { return; }
+	if (!dojo.uri || !dojo.uri.dojoUri){ return dojo.debug("You'll need to load dojo.uri.* for deep debugging - sorry!"); }
+	if (!window.open){ return dojo.debug('Deep debugging is only supported in host environments with window.open'); }
+	var win = window.open(dojo.uri.dojoUri("src/debug/deep.html"), '_blank', 'width=600, height=400, resizable=yes, scrollbars=yes, status=yes');
+	win.debugVar = obj;
+}


Property changes on: trunk/PhotoOp/root/static/js/src/debug.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/dnd/DragAndDrop.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/dnd/DragAndDrop.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/dnd/DragAndDrop.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,175 @@
+/*
+	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.provide("dojo.dnd.DragSource");
+dojo.provide("dojo.dnd.DropTarget");
+dojo.provide("dojo.dnd.DragObject");
+dojo.provide("dojo.dnd.DragAndDrop");
+
+dojo.dnd.DragSource = function(){
+	var dm = dojo.dnd.dragManager;
+	if(dm["registerDragSource"]){ // side-effect prevention
+		dm.registerDragSource(this);
+	}
+}
+
+dojo.lang.extend(dojo.dnd.DragSource, {
+	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);
+	}
+});
+
+dojo.dnd.DragObject = function(){
+	var dm = dojo.dnd.dragManager;
+	if(dm["registerDragObject"]){ // side-effect prevention
+		dm.registerDragObject(this);
+	}
+}
+
+dojo.lang.extend(dojo.dnd.DragObject, {
+	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
+});
+
+dojo.dnd.DropTarget = function(){
+	if (this.constructor == dojo.dnd.DropTarget) { return; } // need to be subclassed
+	this.acceptedTypes = [];
+	dojo.dnd.dragManager.registerDropTarget(this);
+}
+
+dojo.lang.extend(dojo.dnd.DropTarget, {
+
+	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;
+	},
+
+	onDragOver: function(){
+	},
+
+	onDragOut: function(){
+	},
+
+	onDragMove: function(){
+	},
+
+	onDropStart: function(){
+	},
+
+	onDrop: function(){
+	},
+
+	onDropEnd: function(){
+	}
+});
+
+// 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"]
+	//
+}
+
+dojo.dnd.DragManager = function(){
+	/*
+	 *	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.lang.extend(dojo.dnd.DragManager, {
+	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;


Property changes on: trunk/PhotoOp/root/static/js/src/dnd/DragAndDrop.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/dnd/HtmlDragAndDrop.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/dnd/HtmlDragAndDrop.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/dnd/HtmlDragAndDrop.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,475 @@
+/*
+	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.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.dom");
+dojo.require("dojo.style");
+dojo.require("dojo.html");
+dojo.require("dojo.html.extras");
+dojo.require("dojo.lang.extras");
+dojo.require("dojo.lfx.*");
+dojo.require("dojo.event");
+
+dojo.dnd.HtmlDragSource = function(node, type){
+	node = dojo.byId(node);
+	this.dragObjects = [];
+	this.constrainToContainer = false;
+	if(node){
+		this.domNode = node;
+		this.dragObject = node;
+		// register us
+		dojo.dnd.DragSource.call(this);
+		// set properties that might have been clobbered by the mixin
+		this.type = (type)||(this.domNode.nodeName.toLowerCase());
+	}
+}
+dojo.inherits(dojo.dnd.HtmlDragSource, dojo.dnd.DragSource);
+dojo.lang.extend(dojo.dnd.HtmlDragSource, {
+	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.dragObjects.length; i++) {
+			dojo.dnd.dragManager.selectedSources.push(new dojo.dnd.HtmlDragSource(this.dragObjects[i]));
+		}
+	},
+
+	/**
+	* Register elements that should be dragged along with
+	* the actual DragSource.
+	*
+	* Example usage:
+	* 	var dragSource = new dojo.dnd.HtmlDragSource(...);
+	*	// add a single element
+	*	dragSource.addDragObjects(dojo.byId('id1'));
+	*	// add multiple elements to drag along
+	*	dragSource.addDragObjects(dojo.byId('id2'), dojo.byId('id3'));
+	*
+	* el A dom node to add to the drag list.
+	*/
+	addDragObjects: function(/*DOMNode*/ el) {
+		for (var i=0; i<arguments.length; i++) {
+			this.dragObjects.push(arguments[i]);
+		}
+	}
+});
+
+dojo.dnd.HtmlDragObject = function(node, type){
+	this.domNode = dojo.byId(node);
+	this.type = type;
+	this.constrainToContainer = false;
+	this.dragSource = null;
+}
+dojo.inherits(dojo.dnd.HtmlDragObject, dojo.dnd.DragObject);
+dojo.lang.extend(dojo.dnd.HtmlDragObject, {
+	dragClass: "",
+	opacity: 0.5,
+	createIframe: true,		// workaround IE6 bug
+
+	// if true, node will not move in X and/or Y direction
+	disableX: false,
+	disableY: false,
+
+	createDragNode: function() {
+		var node = this.domNode.cloneNode(true);
+		if(this.dragClass) { dojo.html.addClass(node, this.dragClass); }
+		if(this.opacity < 1) { dojo.style.setOpacity(node, this.opacity); }
+		if(node.tagName.toLowerCase() == "tr"){
+			// dojo.debug("Dragging table row")
+			// Create a table for the cloned row
+			var doc = this.domNode.ownerDocument;
+			var table = doc.createElement("table");
+			var tbody = doc.createElement("tbody");
+			tbody.appendChild(node);
+			table.appendChild(tbody);
+
+			// Set a fixed width to the cloned TDs
+			var domTds = this.domNode.childNodes;
+			var cloneTds = node.childNodes;
+			for(var i = 0; i < domTds.length; i++){
+			    if((cloneTds[i])&&(cloneTds[i].style)){
+				    cloneTds[i].style.width = dojo.style.getContentWidth(domTds[i]) + "px";
+			    }
+			}
+			node = table;
+		}
+
+		if((dojo.render.html.ie55||dojo.render.html.ie60) && this.createIframe){
+			with(node.style) {
+				top="0px";
+				left="0px";
+			}
+			var outer = document.createElement("div");
+			outer.appendChild(node);
+			this.bgIframe = new dojo.html.BackgroundIframe(outer);
+			outer.appendChild(this.bgIframe.iframe);
+			node = outer;
+		}
+		node.style.zIndex = 999;
+		return node;
+	},
+
+	onDragStart: function(e){
+		dojo.html.clearSelection();
+
+		this.scrollOffset = dojo.html.getScrollOffset();
+		this.dragStartPosition = dojo.style.getAbsolutePosition(this.domNode, true);
+
+		this.dragOffset = {y: this.dragStartPosition.y - e.pageY,
+			x: this.dragStartPosition.x - e.pageX};
+
+		this.dragClone = this.createDragNode();
+
+		this.containingBlockPosition = this.domNode.offsetParent ? 
+			dojo.style.getAbsolutePosition(this.domNode.offsetParent) : {x:0, y:0};
+
+		if (this.constrainToContainer) {
+			this.constraints = this.getConstraints();
+		}
+
+		// set up for dragging
+		with(this.dragClone.style){
+			position = "absolute";
+			top = this.dragOffset.y + e.pageY + "px";
+			left = this.dragOffset.x + e.pageX + "px";
+		}
+
+		document.body.appendChild(this.dragClone);
+
+		dojo.event.topic.publish('dragStart', { source: this } );
+	},
+
+	/** Return min/max x/y (relative to document.body) for this object) **/
+	getConstraints: function() {
+		if (this.constrainingContainer.nodeName.toLowerCase() == 'body') {
+			var width = dojo.html.getViewportWidth();
+			var height = dojo.html.getViewportHeight();
+			var x = 0;
+			var y = 0;
+		} else {
+			width = dojo.style.getContentWidth(this.constrainingContainer);
+			height = dojo.style.getContentHeight(this.constrainingContainer);
+			x =
+				this.containingBlockPosition.x +
+				dojo.style.getPixelValue(this.constrainingContainer, "padding-left", true) +
+				dojo.style.getBorderExtent(this.constrainingContainer, "left");
+			y =
+				this.containingBlockPosition.y +
+				dojo.style.getPixelValue(this.constrainingContainer, "padding-top", true) +
+				dojo.style.getBorderExtent(this.constrainingContainer, "top");
+		}
+
+		return {
+			minX: x,
+			minY: y,
+			maxX: x + width - dojo.style.getOuterWidth(this.domNode),
+			maxY: y + height - dojo.style.getOuterHeight(this.domNode)
+		}
+	},
+
+	updateDragOffset: function() {
+		var scroll = dojo.html.getScrollOffset();
+		if(scroll.y != this.scrollOffset.y) {
+			var diff = scroll.y - this.scrollOffset.y;
+			this.dragOffset.y += diff;
+			this.scrollOffset.y = scroll.y;
+		}
+		if(scroll.x != this.scrollOffset.x) {
+			var diff = scroll.x - this.scrollOffset.x;
+			this.dragOffset.x += diff;
+			this.scrollOffset.x = scroll.x;
+		}
+	},
+
+	/** Moves the node to follow the mouse */
+	onDragMove: function(e){
+		this.updateDragOffset();
+		var x = this.dragOffset.x + e.pageX;
+		var y = this.dragOffset.y + e.pageY;
+
+		if (this.constrainToContainer) {
+			if (x < this.constraints.minX) { x = this.constraints.minX; }
+			if (y < this.constraints.minY) { y = this.constraints.minY; }
+			if (x > 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 <body>.
+	 */
+	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.dom.removeNode(this.dragClone);
+				this.dragClone = null;
+				break;
+
+			case "dropFailure": // slide back to the start
+				var startCoords = dojo.style.getAbsolutePosition(this.dragClone, true);
+				// offset the end so the effect can be seen
+				var endCoords = [this.dragStartPosition.x + 1,
+					this.dragStartPosition.y + 1];
+
+				// animate
+				var line = new dojo.lfx.Line(startCoords, endCoords);
+				var anim = new dojo.lfx.Animation(500, line, dojo.lfx.easeOut);
+				var dragObject = this;
+				dojo.event.connect(anim, "onAnimate", function(e) {
+					dragObject.dragClone.style.left = e[0] + "px";
+					dragObject.dragClone.style.top = e[1] + "px";
+				});
+				dojo.event.connect(anim, "onEnd", function (e) {
+					// pause for a second (not literally) and disappear
+					dojo.lang.setTimeout(function() {
+							dojo.dom.removeNode(dragObject.dragClone);
+							// Allow drag clone to be gc'ed
+							dragObject.dragClone = null;
+						},
+						200);
+				});
+				anim.play();
+				break;
+		}
+
+		// shortly the browser will fire an onClick() event,
+		// but since this was really a drag, just squelch it
+		dojo.event.connect(this.domNode, "onclick", this, "squelchOnClick");
+
+		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)
+		e.preventDefault();
+
+		// but if a real click comes along, allow it
+		dojo.event.disconnect(this.domNode, "onclick", this, "squelchOnClick");
+	},
+
+	constrainTo: function(container) {
+		this.constrainToContainer=true;
+		if (container) {
+			this.constrainingContainer = container;
+		} else {
+			this.constrainingContainer = this.domNode.parentNode;
+		}
+	}
+});
+
+dojo.dnd.HtmlDropTarget = function(node, types){
+	if (arguments.length == 0) { return; }
+	this.domNode = dojo.byId(node);
+	dojo.dnd.DropTarget.call(this);
+	if(types && dojo.lang.isString(types)) {
+		types = [types];
+	}
+	this.acceptedTypes = types || [];
+}
+dojo.inherits(dojo.dnd.HtmlDropTarget, dojo.dnd.DropTarget);
+
+dojo.lang.extend(dojo.dnd.HtmlDropTarget, {
+	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.dom.ELEMENT_NODE) { continue; }
+			var pos = dojo.style.getAbsolutePosition(child, true);
+			var height = dojo.style.getInnerHeight(child);
+			var width = dojo.style.getInnerWidth(child);
+			this.childBoxes.push({top: pos.y, bottom: pos.y+height,
+				left: pos.x, right: pos.x+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;
+			borderTopWidth = "1px";
+			borderTopColor = "black";
+			borderTopStyle = "solid";
+			width = dojo.style.getInnerWidth(this.domNode) + "px";
+			left = dojo.style.getAbsoluteX(this.domNode, true) + "px";
+		}
+	},
+
+	onDragMove: function(e, dragObjects){
+		var i = this._getNodeUnderMouse(e);
+
+		if(!this.dropIndicator){
+			this.createDropIndicator();
+		}
+
+		if(i < 0) {
+			if(this.childBoxes.length) {
+				var before = (dojo.html.gravity(this.childBoxes[0].node, e) & dojo.html.gravity.NORTH);
+			} else {
+				var before = true;
+			}
+		} else {
+			var child = this.childBoxes[i];
+			var before = (dojo.html.gravity(child.node, e) & dojo.html.gravity.NORTH);
+		}
+		this.placeIndicator(e, dragObjects, i, before);
+
+		if(!dojo.html.hasParent(this.dropIndicator)) {
+			document.body.appendChild(this.dropIndicator);
+		}
+	},
+
+	/**
+	 * Position the horizontal line that indicates "insert between these two items"
+	 */
+	placeIndicator: function(e, dragObjects, boxIndex, before) {
+		with(this.dropIndicator.style){
+			if (boxIndex < 0) {
+				if (this.childBoxes.length) {
+					top = (before ? this.childBoxes[0].top
+						: this.childBoxes[this.childBoxes.length - 1].bottom) + "px";
+				} else {
+					top = dojo.style.getAbsoluteY(this.domNode, true) + "px";
+				}
+			} else {
+				var child = this.childBoxes[boxIndex];
+				top = (before ? child.top : child.bottom) + "px";
+			}
+		}
+	},
+
+	onDragOut: function(e) {
+		if(this.dropIndicator) {
+			dojo.dom.removeNode(this.dropIndicator);
+			delete this.dropIndicator;
+		}
+	},
+
+	/**
+	 * Inserts the DragObject as a child of this node relative to the
+	 * position of the mouse.
+	 *
+	 * @return true if the DragObject was inserted, false otherwise
+	 */
+	onDrop: function(e){
+		this.onDragOut(e);
+
+		var i = this._getNodeUnderMouse(e);
+
+		if (i < 0) {
+			if (this.childBoxes.length) {
+				if (dojo.html.gravity(this.childBoxes[0].node, e) & dojo.html.gravity.NORTH) {
+					return this.insert(e, this.childBoxes[0].node, "before");
+				} else {
+					return this.insert(e, this.childBoxes[this.childBoxes.length-1].node, "after");
+				}
+			}
+			return this.insert(e, this.domNode, "append");
+		}
+
+		var child = this.childBoxes[i];
+		if (dojo.html.gravity(child.node, e) & dojo.html.gravity.NORTH) {
+			return this.insert(e, child.node, "before");
+		} else {
+			return this.insert(e, child.node, "after");
+		}
+	},
+
+	insert: function(e, refNode, position) {
+		var node = e.dragObject.domNode;
+
+		if(position == "before") {
+			return dojo.html.insertBefore(node, refNode);
+		} else if(position == "after") {
+			return dojo.html.insertAfter(node, refNode);
+		} else if(position == "append") {
+			refNode.appendChild(node);
+			return true;
+		}
+
+		return false;
+	}
+});


Property changes on: trunk/PhotoOp/root/static/js/src/dnd/HtmlDragAndDrop.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/dnd/HtmlDragManager.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/dnd/HtmlDragManager.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/dnd/HtmlDragManager.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,475 @@
+/*
+	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.dnd.HtmlDragManager");
+dojo.require("dojo.dnd.DragAndDrop");
+dojo.require("dojo.event.*");
+dojo.require("dojo.lang.array");
+dojo.require("dojo.html");
+dojo.require("dojo.style");
+
+// NOTE: there will only ever be a single instance of HTMLDragManager, so it's
+// safe to use prototype properties for book-keeping.
+dojo.dnd.HtmlDragManager = function(){
+}
+
+dojo.inherits(dojo.dnd.HtmlDragManager, dojo.dnd.DragManager);
+
+dojo.lang.extend(dojo.dnd.HtmlDragManager, {
+	/**
+	 * There are several sets of actions that the DnD code cares about in the
+	 * HTML context:
+	 *	1.) mouse-down ->
+	 *			(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){
+		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.
+			var dp = this.dsPrefix;
+			var dpIdx = dp+"Idx_"+(this.dsCounter++);
+			ds.dragSourceId = dpIdx;
+			this.dragSources[dpIdx] = ds;
+			ds.domNode.setAttribute(dp, dpIdx);
+
+			// so we can drag links
+			if(dojo.render.html.ie){
+				dojo.event.connect(ds.domNode, "ondragstart", this.cancelEvent);
+			}
+		}
+	},
+
+	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.disconnect(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 === document.body){ return; }
+		var ta = dojo.html.getAttribute(tn, this.dsPrefix);
+		while((!ta)&&(tn)){
+			tn = tn.parentNode;
+			if((!tn)||(tn === document.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.dom.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;
+		if((!e.shiftKey)&&(!e.ctrlKey)){
+			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();
+			}
+		}
+
+		dojo.event.disconnect(document, "onmousemove", this, "onMouseMove");
+		this.currentDropTarget = null;
+	},
+
+	onScroll: function(){
+		for(var i = 0; i < this.dragObjects.length; i++) {
+			if(this.dragObjects[i].updateDragOffset) {
+				this.dragObjects[i].updateDragOffset();
+			}
+		}
+		// TODO: do not recalculate, only adjust coordinates
+		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(){
+		this.dropTargetDimensions = [];
+		dojo.lang.forEach(this.dropTargets, function(tempTarget){
+			var tn = tempTarget.domNode;
+			if(!tn){ return; }
+			var ttx = dojo.style.getAbsoluteX(tn, true);
+			var tty = dojo.style.getAbsoluteY(tn, true);
+			this.dropTargetDimensions.push([
+				[ttx, tty],	// upper-left
+				// lower-right
+				[ ttx+dojo.style.getInnerWidth(tn), tty+dojo.style.getInnerHeight(tn) ],
+				tempTarget
+			]);
+			//dojo.debug("Cached for "+tempTarget)
+		}, this);
+		//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.top += dy;
+					tdo.dragOffset.left += 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.dom.hasParent(this.currentDropTarget.domNode))
+			var c = dojo.style.toCoordinateArray(this.currentDropTarget.domNode, true);
+			//		var dtp = this.currentDropTargetPoints;
+			var dtp = [
+				[c[0],c[1]], [c[0]+c[2], c[1]+c[3]]
+			];
+		}
+
+		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;
+	// set up event handlers on the document
+	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
+	dojo.event.connect(window, "onscroll",	dm, "onScroll");
+})();


Property changes on: trunk/PhotoOp/root/static/js/src/dnd/HtmlDragManager.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/dnd/HtmlDragMove.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/dnd/HtmlDragMove.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/dnd/HtmlDragMove.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,76 @@
+/*
+	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.dnd.HtmlDragMove");
+dojo.provide("dojo.dnd.HtmlDragMoveSource");
+dojo.provide("dojo.dnd.HtmlDragMoveObject");
+dojo.require("dojo.dnd.*");
+
+dojo.dnd.HtmlDragMoveSource = function(node, type){
+	dojo.dnd.HtmlDragSource.call(this, node, type);
+}
+dojo.inherits(dojo.dnd.HtmlDragMoveSource, dojo.dnd.HtmlDragSource);
+dojo.lang.extend(dojo.dnd.HtmlDragMoveSource, {
+	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<this.dragObjects.length; i++) {
+			dojo.dnd.dragManager.selectedSources.push(new dojo.dnd.HtmlDragMoveSource(this.dragObjects[i]));
+		}
+	}
+});
+
+dojo.dnd.HtmlDragMoveObject = function(node, type){
+	dojo.dnd.HtmlDragObject.call(this, node, type);
+}
+dojo.inherits(dojo.dnd.HtmlDragMoveObject, dojo.dnd.HtmlDragObject);
+dojo.lang.extend(dojo.dnd.HtmlDragMoveObject, {
+	onDragEnd: function(e){
+		// shortly the browser will fire an onClick() event,
+		// but since this was really a drag, just squelch it
+		dojo.event.connect(this.domNode, "onclick", this, "squelchOnClick");
+	},
+	onDragStart: function(e){
+		dojo.html.clearSelection();
+
+		this.dragClone = this.domNode;
+
+		this.scrollOffset = dojo.html.getScrollOffset();
+		this.dragStartPosition = dojo.style.getAbsolutePosition(this.domNode, true);
+		
+		this.dragOffset = {y: this.dragStartPosition.y - e.pageY,
+			x: this.dragStartPosition.x - e.pageX};
+
+		this.containingBlockPosition = this.domNode.offsetParent ? 
+			dojo.style.getAbsolutePosition(this.domNode.offsetParent, true) : {x:0, y:0};
+
+		this.dragClone.style.position = "absolute";
+
+		if (this.constrainToContainer) {
+			this.constraints = this.getConstraints();
+		}
+	},
+	/**
+	 * Set the position of the drag node.  (x,y) is relative to <body>.
+	 */
+	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"; }
+	}
+});


Property changes on: trunk/PhotoOp/root/static/js/src/dnd/HtmlDragMove.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/dnd/Sortable.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/dnd/Sortable.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/dnd/Sortable.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,28 @@
+/*
+	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.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;
+	}
+
+});


Property changes on: trunk/PhotoOp/root/static/js/src/dnd/Sortable.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/dnd/TreeDragAndDrop.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/dnd/TreeDragAndDrop.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/dnd/TreeDragAndDrop.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,473 @@
+/*
+	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
+*/
+
+/**
+ * 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.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<cloneGrid.length; i++) {
+				cloneGrid.item(i).style.backgroundImage='url()';
+			}
+
+			return result;
+
+
+		});
+
+		dragObject.onDragEnd = function(e) {
+
+			/* restore selection */
+			if (this.savedSelectedNode) {
+				this.savedSelectedNode.markSelected();
+			}
+			//dojo.debug(e.dragStatus);
+
+			return dojo.dnd.HtmlDragObject.prototype.onDragEnd.apply(this, arguments);
+		}
+		//dojo.debug(dragObject.domNode.outerHTML)
+
+
+		return dragObject;
+	},
+
+	onDragEnd: function(e){
+
+
+		 var res = dojo.dnd.HtmlDragSource.prototype.onDragEnd.call(this, e);
+
+
+		 return res;
+	}
+});
+
+// .......................................
+
+dojo.dnd.TreeDropTarget = function(domNode, controller, type, treeNode, DNDMode){
+
+	this.treeNode = treeNode;
+	this.controller = controller; // I will sync-ly process drops
+	this.DNDMode = DNDMode;
+
+	dojo.dnd.HtmlDropTarget.apply(this, [domNode, type]);
+}
+
+dojo.inherits(dojo.dnd.TreeDropTarget, dojo.dnd.HtmlDropTarget);
+
+dojo.lang.extend(dojo.dnd.TreeDropTarget, {
+
+	autoExpandDelay: 1500,
+	autoExpandTimer: null,
+
+
+	position: null,
+
+	indicatorStyle: "2px black solid",
+
+	showIndicator: function(position) {
+
+		// do not change style too often, cause of blinking possible
+		if (this.position == position) {
+			return;
+		}
+
+		//dojo.debug(position)
+
+		this.hideIndicator();
+
+		this.position = position;
+
+		if (position == "before") {
+			this.treeNode.labelNode.style.borderTop = this.indicatorStyle;
+		} else if (position == "after") {
+			this.treeNode.labelNode.style.borderBottom = this.indicatorStyle;
+		} else if (position == "onto") {
+			this.treeNode.markSelected();
+		}
+
+
+	},
+
+	hideIndicator: function() {
+		this.treeNode.labelNode.style.borderBottom="";
+		this.treeNode.labelNode.style.borderTop="";
+		this.treeNode.unMarkSelected();
+		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();
+		}
+
+		return accepts;
+	},
+
+	/* Parent.onDragOver calls this function to get accepts status */
+	accepts: function(dragObjects) {
+
+		var accepts = dojo.dnd.HtmlDropTarget.prototype.accepts.apply(this, arguments);
+
+		if (!accepts) return false;
+
+		var sourceTreeNode = dragObjects[0].treeNode;
+
+		if (dojo.lang.isUndefined(sourceTreeNode) || !sourceTreeNode || !sourceTreeNode.isTreeNode) {
+			dojo.raise("Source is not TreeNode or not found");
+		}
+
+		if (sourceTreeNode === this.treeNode) return false;
+
+		return true;
+	},
+
+
+
+	setAutoExpandTimer: function() {
+		// set up autoexpand timer
+		var _this = this;
+
+		var autoExpand = function () {
+			if (dojo.dnd.dragManager.currentDropTarget === _this) {
+				_this.controller.expand(_this.treeNode);
+			}
+		}
+
+		this.autoExpandTimer = dojo.lang.setTimeout(autoExpand, _this.autoExpandDelay);
+	},
+
+
+	getAcceptPosition: function(e, sourceTreeNode) {
+
+		var DNDMode = this.DNDMode;
+
+		if (DNDMode & dojo.widget.Tree.prototype.DNDModes.ONTO &&
+			// check if ONTO is allowed localy
+			!(
+			  !this.treeNode.actionIsDisabled(dojo.widget.TreeNode.prototype.actions.ADDCHILD) // check dynamically cause may change w/o regeneration of dropTarget
+			  && sourceTreeNode.parent !== this.treeNode
+			  && this.controller.canMove(sourceTreeNode, this.treeNode)
+			 )
+		) {
+			// disable ONTO if can't move
+			DNDMode &= ~dojo.widget.Tree.prototype.DNDModes.ONTO;
+		}
+
+
+		var position = this.getPosition(e, DNDMode);
+
+		//dojo.debug(DNDMode & +" : "+position);
+
+
+		// if onto is here => 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) {
+		node = dojo.byId(this.treeNode.labelNode);
+		var mousey = e.pageY || e.clientY + document.body.scrollTop;
+		var nodey = dojo.html.getAbsoluteY(node);
+		var height = dojo.html.getInnerHeight(node);
+
+		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, node.tree.DNDMode);
+
+		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];
+		}
+	}
+
+
+
+
+
+});


Property changes on: trunk/PhotoOp/root/static/js/src/dnd/TreeDragAndDrop.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/dnd/__package__.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/dnd/__package__.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/dnd/__package__.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,16 @@
+/*
+	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.kwCompoundRequire({
+	common: ["dojo.dnd.DragAndDrop"],
+	browser: ["dojo.dnd.HtmlDragAndDrop"],
+	dashboard: ["dojo.dnd.HtmlDragAndDrop"]
+});
+dojo.provide("dojo.dnd.*");


Property changes on: trunk/PhotoOp/root/static/js/src/dnd/__package__.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/doc.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/doc.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/doc.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -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


Property changes on: trunk/PhotoOp/root/static/js/src/doc.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/dom.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/dom.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/dom.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,485 @@
+/*
+	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.dom");
+dojo.require("dojo.lang.array");
+
+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 == "object") {
+		try {
+			return wh instanceof Element;
+		} catch(E) {}
+	} else {
+		// best-guess
+		return wh && !isNaN(wh.nodeType);
+	}
+}
+
+dojo.dom.getTagName = function(node){
+	dojo.deprecated("dojo.dom.getTagName", "use node.tagName instead", "0.4");
+
+	var tagName = node.tagName;
+	if(tagName.substr(0,5).toLowerCase()!="dojo:"){
+		
+		if(tagName.substr(0,4).toLowerCase()=="dojo"){
+			// FIXME: this assuumes tag names are always lower case
+			return "dojo:" + tagName.substring(4).toLowerCase();
+		}
+
+		// allow lower-casing
+		var djt = node.getAttribute("dojoType")||node.getAttribute("dojotype");
+		if(djt){
+			return "dojo:"+djt.toLowerCase();
+		}
+		
+		if((node.getAttributeNS)&&(node.getAttributeNS(this.dojoml,"type"))){
+			return "dojo:" + node.getAttributeNS(this.dojoml,"type").toLowerCase();
+		}
+		try{
+			// FIXME: IE really really doesn't like this, so we squelch
+			// errors for it
+			djt = node.getAttribute("dojo:type");
+		}catch(e){ /* FIXME: log? */ }
+		if(djt){
+			return "dojo:"+djt.toLowerCase();
+		}
+
+		if((!dj_global["djConfig"])||(!djConfig["ignoreClassNames"])){
+			// FIXME: should we make this optionally enabled via djConfig?
+			var classes = node.className||node.getAttribute("class");
+			// FIXME: following line, without check for existence of classes.indexOf
+			// breaks firefox 1.5's svg widgets
+			if((classes)&&(classes.indexOf)&&(classes.indexOf("dojo-") != -1)){
+				var aclasses = classes.split(" ");
+				for(var x=0; x<aclasses.length; x++){
+					if((aclasses[x].length>5)&&(aclasses[x].indexOf("dojo-")>=0)){
+						return "dojo:"+aclasses[x].substr(5).toLowerCase();
+					}
+				}
+			}
+		}
+
+	}
+	return tagName.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(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 = dojo.lang.isFunction(filterFunction);
+	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;
+
+	if(!dj_undef("ActiveXObject")){
+		var prefixes = [ "MSXML2", "Microsoft", "MSXML", "MSXML3" ];
+		for(var i = 0; i<prefixes.length; i++){
+			try{
+				doc = new ActiveXObject(prefixes[i]+".XMLDOM");
+			}catch(e){ /* squelch */ };
+
+			if(doc){ break; }
+		}
+	}else if((document.implementation)&&
+		(document.implementation.createDocument)){
+		doc = document.implementation.createDocument("", "", null);
+	}
+	
+	return doc;
+}
+
+dojo.dom.createDocumentFromText = function(str, mimetype){
+	if(!mimetype){ mimetype = "text/xml"; }
+	if(!dj_undef("DOMParser")){
+		var parser = new DOMParser();
+		return parser.parseFromString(str, mimetype);
+	}else if(!dj_undef("ActiveXObject")){
+		var domDoc = dojo.dom.createDocument();
+		if(domDoc){
+			domDoc.async = false;
+			domDoc.loadXML(str);
+			return domDoc;
+		}else{
+			dojo.debug("toXml didn't work?");
+		}
+	/*
+	}else if((dojo.render.html.capable)&&(dojo.render.html.safari)){
+		// FIXME: this doesn't appear to work!
+		// from: http://web-graphics.com/mtarchive/001606.php
+		// var xml = '<?xml version="1.0"?>'+str;
+		var mtype = "text/xml";
+		var xml = '<?xml version="1.0"?>'+str;
+		var url = "data:"+mtype+";charset=utf-8,"+encodeURIComponent(xml);
+		var req = new XMLHttpRequest();
+		req.open("GET", url, false);
+		req.overrideMimeType(mtype);
+		req.send(null);
+		return req.responseXML;
+	*/
+	}else if(document.createElement){
+		// FIXME: this may change all tags to uppercase!
+		var tmp = document.createElement("xml");
+		tmp.innerHTML = str;
+		if(document.implementation && document.implementation.createDocument) {
+			var xmlDoc = document.implementation.createDocument("foo", "", null);
+			for(var i = 0; i < tmp.childNodes.length; i++) {
+				xmlDoc.importNode(tmp.childNodes.item(i), true);
+			}
+			return xmlDoc;
+		}
+		// FIXME: probably not a good idea to have to return an HTML fragment
+		// FIXME: the tmp.doc.firstChild is as tested from IE, so it may not
+		// work that way across the board
+		return ((tmp.document)&&
+			(tmp.document.firstChild ?  tmp.document.firstChild : tmp));
+	}
+	return null;
+}
+
+dojo.dom.prependChild = function(node, parent) {
+	if(parent.firstChild) {
+		parent.insertBefore(node, parent.firstChild);
+	} else {
+		parent.appendChild(node);
+	}
+	return true;
+}
+
+dojo.dom.insertBefore = function(node, ref, force){
+	if (force != true &&
+		(node === ref || node.nextSibling === ref)){ return false; }
+	var parent = ref.parentNode;
+	parent.insertBefore(node, ref);
+	return true;
+}
+
+dojo.dom.insertAfter = function(node, ref, force){
+	var pn = ref.parentNode;
+	if(ref == pn.lastChild){
+		if((force != true)&&(node === ref)){
+			return false;
+		}
+		pn.appendChild(node);
+	}else{
+		return this.insertBefore(node, ref.nextSibling, force);
+	}
+	return true;
+}
+
+dojo.dom.insertAtPosition = function(node, ref, position){
+	if((!node)||(!ref)||(!position)){ return false; }
+	switch(position.toLowerCase()){
+		case "before":
+			return dojo.dom.insertBefore(node, ref);
+		case "after":
+			return dojo.dom.insertAfter(node, ref);
+		case "first":
+			if(ref.firstChild){
+				return dojo.dom.insertBefore(node, ref.firstChild);
+			}else{
+				ref.appendChild(node);
+				return true;
+			}
+			break;
+		default: // aka: last
+			ref.appendChild(node);
+			return true;
+	}
+}
+
+dojo.dom.insertAtIndex = function(node, containingNode, insertionIndex){
+	var siblingNodes = containingNode.childNodes;
+
+	// if there aren't any kids yet, just add it to the beginning
+
+	if (!siblingNodes.length){
+		containingNode.appendChild(node);
+		return true;
+	}
+
+	// otherwise we need to walk the childNodes
+	// and find our spot
+
+	var after = null;
+
+	for(var i=0; i<siblingNodes.length; i++){
+
+		var sibling_index = siblingNodes.item(i)["getAttribute"] ? parseInt(siblingNodes.item(i).getAttribute("dojoinsertionindex")) : -1;
+
+		if (sibling_index < insertionIndex){
+			after = siblingNodes.item(i);
+		}
+	}
+
+	if (after){
+		// add it after the node in {after}
+
+		return dojo.dom.insertAfter(node, after);
+	}else{
+		// add it to the start
+
+		return dojo.dom.insertBefore(node, siblingNodes.item(0));
+	}
+}
+	
+/**
+ * implementation of the DOM Level 3 attribute.
+ * 
+ * @param node The node to scan for text
+ * @param text Optional, set the text to this value.
+ */
+dojo.dom.textContent = function(node, text){
+	if (text) {
+		dojo.dom.replaceChildren(node, document.createTextNode(text));
+		return text;
+	} else {
+		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.collectionToArray = function(collection){
+	dojo.deprecated("dojo.dom.collectionToArray", "use dojo.lang.toArray instead", "0.4");
+	return dojo.lang.toArray(collection);
+}
+
+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 = <foo />
+ * 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) {
+		var arr = dojo.lang.toArray(arguments, 1);
+		return arr[ dojo.lang.find(node.tagName, arr) ] || "";
+	}
+	return "";
+}


Property changes on: trunk/PhotoOp/root/static/js/src/dom.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/event/__package__.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/event/__package__.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/event/__package__.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,16 @@
+/*
+	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.kwCompoundRequire({
+	common: ["dojo.event", "dojo.event.topic"],
+	browser: ["dojo.event.browser"],
+	dashboard: ["dojo.event.browser"]
+});
+dojo.provide("dojo.event.*");


Property changes on: trunk/PhotoOp/root/static/js/src/event/__package__.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/event/browser.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/event/browser.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/event/browser.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,273 @@
+/*
+	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.event.browser");
+dojo.require("dojo.event");
+
+// FIXME: any particular reason this is in the global scope?
+dojo._ie_clobber = new function(){
+	this.clobberNodes = [];
+
+	function nukeProp(node, prop){
+		// try{ node.removeAttribute(prop); 	}catch(e){ /* squelch */ }
+		try{ node[prop] = null; 			}catch(e){ /* squelch */ }
+		try{ delete node[prop]; 			}catch(e){ /* squelch */ }
+		// FIXME: JotLive needs this, but I'm not sure if it's too slow or not
+		try{ node.removeAttribute(prop);	}catch(e){ /* squelch */ }
+	}
+
+	this.clobber = function(nodeRef){
+		var na;
+		var tna;
+		if(nodeRef){
+			tna = nodeRef.all || nodeRef.getElementsByTagName("*");
+			na = [nodeRef];
+			for(var x=0; x<tna.length; x++){
+				// if we're gonna be clobbering the thing, at least make sure
+				// we aren't trying to do it twice
+				if(tna[x]["__doClobber__"]){
+					na.push(tna[x]);
+				}
+			}
+		}else{
+			try{ window.onload = null; }catch(e){}
+			na = (this.clobberNodes.length) ? this.clobberNodes : document.all;
+		}
+		tna = null;
+		var basis = {};
+		for(var i = na.length-1; i>=0; i=i-1){
+			var el = na[i];
+			if(el["__clobberAttrs__"]){
+				for(var j=0; j<el.__clobberAttrs__.length; j++){
+					nukeProp(el, el.__clobberAttrs__[j]);
+				}
+				nukeProp(el, "__clobberAttrs__");
+				nukeProp(el, "__doClobber__");
+			}
+		}
+		na = null;
+	}
+}
+
+if(dojo.render.html.ie){
+	dojo.addOnUnload(function(){
+		dojo._ie_clobber.clobber();
+		try{
+			if((dojo["widget"])&&(dojo.widget["manager"])){
+				dojo.widget.manager.destroyAll();
+			}
+		}catch(e){}
+		try{ window.onload = null; }catch(e){}
+		try{ window.onunload = null; }catch(e){}
+		dojo._ie_clobber.clobberNodes = [];
+		// CollectGarbage();
+	});
+}
+
+dojo.event.browser = new function(){
+
+	var clobberIdx = 0;
+
+	this.clean = function(node){
+		if(dojo.render.html.ie){ 
+			dojo._ie_clobber.clobber(node);
+		}
+	}
+
+	this.addClobberNode = function(node){
+		if(!dojo.render.html.ie){ return; }
+		if(!node["__doClobber__"]){
+			node.__doClobber__ = true;
+			dojo._ie_clobber.clobberNodes.push(node);
+			// this might not be the most efficient thing to do, but it's
+			// much less error prone than other approaches which were
+			// previously tried and failed
+			node.__clobberAttrs__ = [];
+		}
+	}
+
+	this.addClobberNodeAttrs = function(node, props){
+		if(!dojo.render.html.ie){ return; }
+		this.addClobberNode(node);
+		for(var x=0; x<props.length; x++){
+			node.__clobberAttrs__.push(props[x]);
+		}
+	}
+
+	this.removeListener = function(node, evtName, fp, capture){
+		if(!capture){ var capture = false; }
+		evtName = evtName.toLowerCase();
+		if(evtName.substr(0,2)=="on"){ evtName = evtName.substr(2); }
+		// FIXME: this is mostly a punt, we aren't actually doing anything on IE
+		if(node.removeEventListener){
+			node.removeEventListener(evtName, fp, capture);
+		}
+	}
+
+	this.addListener = function(node, evtName, fp, capture, dontFix){
+		if(!node){ return; } // FIXME: log and/or bail?
+		if(!capture){ var capture = false; }
+		evtName = evtName.toLowerCase();
+		if(evtName.substr(0,2)!="on"){ evtName = "on"+evtName; }
+
+		if(!dontFix){
+			// build yet another closure around fp in order to inject fixEvent
+			// around the resulting event
+			var newfp = function(evt){
+				if(!evt){ evt = window.event; }
+				var ret = fp(dojo.event.browser.fixEvent(evt, this));
+				if(capture){
+					dojo.event.browser.stopEvent(evt);
+				}
+				return ret;
+			}
+		}else{
+			newfp = fp;
+		}
+
+		if(node.addEventListener){ 
+			node.addEventListener(evtName.substr(2), newfp, capture);
+			return newfp;
+		}else{
+			if(typeof node[evtName] == "function" ){
+				var oldEvt = node[evtName];
+				node[evtName] = function(e){
+					oldEvt(e);
+					return newfp(e);
+				}
+			}else{
+				node[evtName]=newfp;
+			}
+			if(dojo.render.html.ie){
+				this.addClobberNodeAttrs(node, [evtName]);
+			}
+			return newfp;
+		}
+	}
+
+	this.isEvent = function(obj){
+		// FIXME: event detection hack ... could test for additional attributes
+		// if necessary
+		return (typeof obj != "undefined")&&(typeof Event != "undefined")&&(obj.eventPhase);
+		// Event does not support instanceof in Opera, otherwise:
+		//return (typeof Event != "undefined")&&(obj instanceof Event);
+	}
+
+	this.currentEvent = null;
+	
+	this.callListener = function(listener, curTarget){
+		if(typeof listener != 'function'){
+			dojo.raise("listener not a function: " + listener);
+		}
+		dojo.event.browser.currentEvent.currentTarget = curTarget;
+		return listener.call(curTarget, dojo.event.browser.currentEvent);
+	}
+
+	this.stopPropagation = function(){
+		dojo.event.browser.currentEvent.cancelBubble = true;
+	}
+
+	this.preventDefault = function(){
+	  dojo.event.browser.currentEvent.returnValue = false;
+	}
+
+	this.keys = {
+		KEY_BACKSPACE: 8,
+		KEY_TAB: 9,
+		KEY_ENTER: 13,
+		KEY_SHIFT: 16,
+		KEY_CTRL: 17,
+		KEY_ALT: 18,
+		KEY_PAUSE: 19,
+		KEY_CAPS_LOCK: 20,
+		KEY_ESCAPE: 27,
+		KEY_SPACE: 32,
+		KEY_PAGE_UP: 33,
+		KEY_PAGE_DOWN: 34,
+		KEY_END: 35,
+		KEY_HOME: 36,
+		KEY_LEFT_ARROW: 37,
+		KEY_UP_ARROW: 38,
+		KEY_RIGHT_ARROW: 39,
+		KEY_DOWN_ARROW: 40,
+		KEY_INSERT: 45,
+		KEY_DELETE: 46,
+		KEY_LEFT_WINDOW: 91,
+		KEY_RIGHT_WINDOW: 92,
+		KEY_SELECT: 93,
+		KEY_F1: 112,
+		KEY_F2: 113,
+		KEY_F3: 114,
+		KEY_F4: 115,
+		KEY_F5: 116,
+		KEY_F6: 117,
+		KEY_F7: 118,
+		KEY_F8: 119,
+		KEY_F9: 120,
+		KEY_F10: 121,
+		KEY_F11: 122,
+		KEY_F12: 123,
+		KEY_NUM_LOCK: 144,
+		KEY_SCROLL_LOCK: 145
+	};
+
+	// reverse lookup
+	this.revKeys = [];
+	for(var key in this.keys){
+		this.revKeys[this.keys[key]] = key;
+	}
+
+	this.fixEvent = function(evt, sender){
+		if((!evt)&&(window["event"])){
+			var evt = window.event;
+		}
+		
+		if((evt["type"])&&(evt["type"].indexOf("key") == 0)){ // key events
+			evt.keys = this.revKeys;
+			// FIXME: how can we eliminate this iteration?
+			for(var key in this.keys) {
+				evt[key] = this.keys[key];
+			}
+			if((dojo.render.html.ie)&&(evt["type"] == "keypress")){
+				evt.charCode = evt.keyCode;
+			}
+		}
+	
+		if(dojo.render.html.ie){
+			if(!evt.target){ evt.target = evt.srcElement; }
+			if(!evt.currentTarget){ evt.currentTarget = (sender ? sender : evt.srcElement); }
+			if(!evt.layerX){ evt.layerX = evt.offsetX; }
+			if(!evt.layerY){ evt.layerY = evt.offsetY; }
+			// FIXME: scroll position query is duped from dojo.html to avoid dependency on that entire module
+			var docBody = ((dojo.render.html.ie55)||(document["compatMode"] == "BackCompat")) ? document.body : document.documentElement;
+			if(!evt.pageX){ evt.pageX = evt.clientX + (docBody.scrollLeft || 0) }
+			if(!evt.pageY){ evt.pageY = evt.clientY + (docBody.scrollTop || 0) }
+			// mouseover
+			if(evt.type == "mouseover"){ evt.relatedTarget = evt.fromElement; }
+			// mouseout
+			if(evt.type == "mouseout"){ evt.relatedTarget = evt.toElement; }
+			this.currentEvent = evt;
+			evt.callListener = this.callListener;
+			evt.stopPropagation = this.stopPropagation;
+			evt.preventDefault = this.preventDefault;
+		}
+		return evt;
+	}
+
+	this.stopEvent = function(ev) {
+		if(window.event){
+			ev.returnValue = false;
+			ev.cancelBubble = true;
+		}else{
+			ev.preventDefault();
+			ev.stopPropagation();
+		}
+	}
+}


Property changes on: trunk/PhotoOp/root/static/js/src/event/browser.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/event/topic.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/event/topic.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/event/topic.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,99 @@
+/*
+	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.event");
+dojo.provide("dojo.event.topic");
+
+dojo.event.topic = new function(){
+	this.topics = {};
+
+	this.getTopic = function(topicName){
+		if(!this.topics[topicName]){
+			this.topics[topicName] = new this.TopicImpl(topicName);
+		}
+		return this.topics[topicName];
+	}
+
+	this.registerPublisher = function(topic, obj, funcName){
+		var topic = this.getTopic(topic);
+		topic.registerPublisher(obj, funcName);
+	}
+
+	this.subscribe = function(topic, obj, funcName){
+		var topic = this.getTopic(topic);
+		topic.subscribe(obj, funcName);
+	}
+
+	this.unsubscribe = function(topic, obj, funcName){
+		var topic = this.getTopic(topic);
+		topic.unsubscribe(obj, funcName);
+	}
+
+	this.destroy = function(topic){
+		this.getTopic(topic).destroy();
+		delete this.topics[topic];
+	}
+
+	this.publishApply = function(topic, args){
+		var topic = this.getTopic(topic);
+		topic.sendMessage.apply(topic, args);
+	}
+
+	this.publish = function(topic, message){
+		var topic = this.getTopic(topic);
+		// if message is an array, we treat it as a set of arguments,
+		// otherwise, we just pass on the arguments passed in as-is
+		var args = [];
+		// could we use concat instead here?
+		for(var x=1; x<arguments.length; x++){
+			args.push(arguments[x]);
+		}
+		topic.sendMessage.apply(topic, args);
+	}
+}
+
+dojo.event.topic.TopicImpl = function(topicName){
+	this.topicName = topicName;
+
+	this.subscribe = function(listenerObject, listenerMethod){
+		var tf = listenerMethod||listenerObject;
+		var to = (!listenerMethod) ? dj_global : listenerObject;
+		dojo.event.kwConnect({
+			srcObj:		this, 
+			srcFunc:	"sendMessage", 
+			adviceObj:	to,
+			adviceFunc: tf
+		});
+	}
+
+	this.unsubscribe = function(listenerObject, listenerMethod){
+		var tf = (!listenerMethod) ? listenerObject : listenerMethod;
+		var to = (!listenerMethod) ? null : listenerObject;
+		dojo.event.kwDisconnect({
+			srcObj:		this, 
+			srcFunc:	"sendMessage", 
+			adviceObj:	to,
+			adviceFunc: tf
+		});
+	}
+
+	this.destroy = function(){
+		dojo.event.MethodJoinPoint.getForMethod(this, "sendMessage").disconnect();
+	}
+
+	this.registerPublisher = function(publisherObject, publisherMethod){
+		dojo.event.connect(publisherObject, publisherMethod, this, "sendMessage");
+	}
+
+	this.sendMessage = function(message){
+		// The message has been propagated
+	}
+}
+


Property changes on: trunk/PhotoOp/root/static/js/src/event/topic.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/event.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/event.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/event.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,575 @@
+/*
+	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.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"]);
+
+	// FIXME: where should we put this method (not here!)?
+	function interpolateArgs(args, searchForNames){
+		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 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);
+		}
+		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<arguments.length; x++){
+				argsStr.push(arguments[x]);
+			}
+			dojo.debug("("+kwArgs.srcObj+")."+kwArgs.srcFunc, ":", argsStr.join(", "));
+		}
+		this.kwConnect(kwArgs);
+	}
+
+	this.connectBefore = function(){
+		var args = ["before"];
+		for(var i = 0; i < arguments.length; i++) { args.push(arguments[i]); }
+		return this.connect.apply(this, args);
+	}
+
+	this.connectAround = function(){
+		var args = ["around"];
+		for(var i = 0; i < arguments.length; i++) { args.push(arguments[i]); }
+		return this.connect.apply(this, args);
+	}
+
+	this.connectOnce = function(){
+		var ao = interpolateArgs(arguments, true);
+		ao.once = true;
+		return this.connect(ao);
+	}
+
+	this._kwConnectImpl = function(kwArgs, disconnect){
+		var fn = (disconnect) ? "disconnect" : "connect";
+		if(typeof kwArgs["srcFunc"] == "function"){
+			kwArgs.srcObj = kwArgs["srcObj"]||dj_global;
+			var tmpName  = dojo.lang.nameAnonFunc(kwArgs.srcFunc, kwArgs.srcObj, true);
+			kwArgs.srcFunc = tmpName;
+		}
+		if(typeof kwArgs["adviceFunc"] == "function"){
+			kwArgs.adviceObj = kwArgs["adviceObj"]||dj_global;
+			var tmpName  = dojo.lang.nameAnonFunc(kwArgs.adviceFunc, kwArgs.adviceObj, true);
+			kwArgs.adviceFunc = tmpName;
+		}
+		return dojo.event[fn](	(kwArgs["type"]||kwArgs["adviceType"]||"after"),
+									kwArgs["srcObj"]||dj_global,
+									kwArgs["srcFunc"],
+									kwArgs["adviceObj"]||kwArgs["targetObj"]||dj_global,
+									kwArgs["adviceFunc"]||kwArgs["targetFunc"],
+									kwArgs["aroundObj"],
+									kwArgs["aroundFunc"],
+									kwArgs["once"],
+									kwArgs["delay"],
+									kwArgs["rate"],
+									kwArgs["adviceMsg"]||false );
+	}
+
+	this.kwConnect = function(kwArgs){
+		return this._kwConnectImpl(kwArgs, false);
+
+	}
+
+	this.disconnect = function(){
+		var ao = interpolateArgs(arguments, true);
+		if(!ao.adviceFunc){ return; } // nothing to disconnect
+		var mjp = dojo.event.MethodJoinPoint.getForMethod(ao.srcObj, ao.srcFunc);
+		return mjp.removeAdvice(ao.adviceObj, ao.adviceFunc, ao.adviceType, ao.once);
+	}
+
+	this.kwDisconnect = function(kwArgs){
+		return this._kwConnectImpl(kwArgs, true);
+	}
+}
+
+// exactly one of these is created whenever a method with a joint point is run,
+// if there is at least one 'around' advice.
+dojo.event.MethodInvocation = function(join_point, obj, args) {
+	this.jp_ = join_point;
+	this.object = obj;
+	this.args = [];
+	for(var x=0; x<args.length; x++){
+		this.args[x] = args[x];
+	}
+	// the index of the 'around' that is currently being executed.
+	this.around_index = -1;
+}
+
+dojo.event.MethodInvocation.prototype.proceed = function() {
+	this.around_index++;
+	if(this.around_index >= 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{
+						evt = window.event;
+					}
+				}catch(e){
+					evt = window.event;
+				}
+
+				if(evt){
+					args.push(dojo.event.browser.fixEvent(evt, this));
+				}
+			}else{
+				for(var x=0; x<arguments.length; x++){
+					if((x==0)&&(isNode)&&(dojo.event.browser.isEvent(arguments[x]))){
+						args.push(dojo.event.browser.fixEvent(arguments[x], this));
+					}else{
+						args.push(arguments[x]);
+					}
+				}
+			}
+			// return joinpoint.run.apply(joinpoint, arguments); 
+			return joinpoint.run.apply(joinpoint, args); 
+		}
+		obj[methname].__preJoinArity = origArity;
+	}
+	return joinpoint;
+}
+
+dojo.lang.extend(dojo.event.MethodJoinPoint, {
+	unintercept: function(){
+		this.object[this.methodname] = this.methodfunc;
+		this.before = [];
+		this.after = [];
+		this.around = [];
+	},
+
+	disconnect: dojo.lang.forward("unintercept"),
+
+	run: function() {
+		var obj = this.object||dj_global;
+		var args = arguments;
+
+		// optimization. We only compute once the array version of the arguments
+		// pseudo-arr in order to prevent building it each time advice is unrolled.
+		var aargs = [];
+		for(var x=0; x<args.length; x++){
+			aargs[x] = args[x];
+		}
+
+		var unrollAdvice  = function(marr){ 
+			if(!marr){
+				dojo.debug("Null argument to unrollAdvice()");
+				return;
+			}
+		  
+			var callObj = marr[0]||dj_global;
+			var callFunc = marr[1];
+			
+			if(!callObj[callFunc]){
+				dojo.raise("function \"" + callFunc + "\" does not exist on \"" + callObj + "\"");
+			}
+			
+			var aroundObj = marr[2]||dj_global;
+			var aroundFunc = marr[3];
+			var msg = marr[6];
+			var undef;
+
+			var to = {
+				args: [],
+				jp_: this,
+				object: obj,
+				proceed: function(){
+					return callObj[callFunc].apply(callObj, to.args);
+				}
+			};
+			to.args = aargs;
+
+			var delay = parseInt(marr[4]);
+			var hasDelay = ((!isNaN(delay))&&(marr[4]!==null)&&(typeof marr[4] != "undefined"));
+			if(marr[5]){
+				var rate = parseInt(marr[5]);
+				var cur = new Date();
+				var timerSet = false;
+				if((marr["last"])&&((cur-marr.last)<=rate)){
+					if(dojo.event.canTimeout){
+						if(marr["delayTimer"]){
+							clearTimeout(marr.delayTimer);
+						}
+						var tod = parseInt(rate*2); // is rate*2 naive?
+						var mcpy = dojo.lang.shallowCopy(marr);
+						marr.delayTimer = setTimeout(function(){
+							// FIXME: on IE at least, event objects from the
+							// browser can go out of scope. How (or should?) we
+							// deal with it?
+							mcpy[5] = 0;
+							unrollAdvice(mcpy);
+						}, tod);
+					}
+					return;
+				}else{
+					marr.last = cur;
+				}
+			}
+
+			// FIXME: need to enforce rates for a connection here!
+
+			if(aroundFunc){
+				// NOTE: around advice can't delay since we might otherwise depend
+				// on execution order!
+				aroundObj[aroundFunc].call(aroundObj, to);
+			}else{
+				// var tmjp = dojo.event.MethodJoinPoint.getForMethod(obj, methname);
+				if((hasDelay)&&((dojo.render.html)||(dojo.render.svg))){  // FIXME: the render checks are grotty!
+					dj_global["setTimeout"](function(){
+						if(msg){
+							callObj[callFunc].call(callObj, to); 
+						}else{
+							callObj[callFunc].apply(callObj, args); 
+						}
+					}, delay);
+				}else{ // many environments can't support delay!
+					if(msg){
+						callObj[callFunc].call(callObj, to); 
+					}else{
+						callObj[callFunc].apply(callObj, args); 
+					}
+				}
+			}
+		}
+
+		if(this.before.length>0){
+			dojo.lang.forEach(this.before, 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){
+			dojo.lang.forEach(this.after, 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<arr.length; x++){
+			var aao = (typeof thisAdvice == "object") ? (new String(thisAdvice)).toString() : thisAdvice;
+			var a1o = (typeof arr[x][1] == "object") ? (new String(arr[x][1])).toString() : arr[x][1];
+			if((arr[x][0] == thisAdviceObj)&&(a1o == aao)){
+				ind = x;
+			}
+		}
+		return ind;
+	},
+
+	removeAdvice: function(thisAdviceObj, thisAdvice, advice_kind, once){
+		var arr = this.getArr(advice_kind);
+		var ind = this.hasAdvice(thisAdviceObj, thisAdvice, advice_kind, arr);
+		if(ind == -1){
+			return false;
+		}
+		while(ind != -1){
+			arr.splice(ind, 1);
+			if(once){ break; }
+			ind = this.hasAdvice(thisAdviceObj, thisAdvice, advice_kind, arr);
+		}
+		return true;
+	}
+});


Property changes on: trunk/PhotoOp/root/static/js/src/event.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/experimental.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/experimental.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/experimental.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,21 @@
+/*
+	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.experimental");
+
+/**
+ * Convenience for informing of experimental code.
+ */
+dojo.experimental = function(packageName, extra){
+	var mess = "EXPERIMENTAL: " + packageName;
+	mess += " -- Not yet ready for use.  APIs subject to change without notice.";
+	if(extra){ mess += " " + extra; }
+	dojo.debug(mess);
+}


Property changes on: trunk/PhotoOp/root/static/js/src/experimental.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/flash/flash6/DojoExternalInterface.as
===================================================================
--- trunk/PhotoOp/root/static/js/src/flash/flash6/DojoExternalInterface.as	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/flash/flash6/DojoExternalInterface.as	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,214 @@
+/*
+	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
+*/
+
+/** 
+		An implementation of Flash 8's ExternalInterface that works with Flash 6
+		and which is source-compatible with Flash 8. 
+		
+		@author Brad Neuberg, bkn3 at columbia.edu 
+*/
+
+class DojoExternalInterface{
+	public static var available:Boolean;
+	public static var dojoPath = "";
+	
+	public static var _fscommandReady = false;
+	public static var _callbacks = new Array();
+
+	public static function initialize(){ 
+		//getURL("javascript:dojo.debug('FLASH:DojoExternalInterface initialize')");
+		// FIXME: Set available variable by testing for capabilities
+		DojoExternalInterface.available = true;
+		
+		// extract the dojo base path
+		DojoExternalInterface.dojoPath = DojoExternalInterface.getDojoPath();
+		//getURL("javascript:dojo.debug('FLASH:dojoPath="+DojoExternalInterface.dojoPath+"')");
+		
+		// Sometimes, on IE, the fscommand infrastructure can take a few hundred
+		// milliseconds the first time a page loads. Set a timer to keep checking
+		// to make sure we can issue fscommands; otherwise, our calls to fscommand
+		// for setCallback() and loaded() will just "disappear"
+		_root.fscommandReady = false;
+		var fsChecker = function(){
+			// issue a test fscommand
+			fscommand("fscommandReady");
+			
+			// JavaScript should set _root.fscommandReady if it got the call
+			if(_root.fscommandReady == "true"){
+				DojoExternalInterface._fscommandReady = true;
+				clearInterval(_root.fsTimer);
+			}
+		};
+		_root.fsTimer = setInterval(fsChecker, 100);
+	}
+	
+	public static function addCallback(methodName:String, instance:Object, 
+											method:Function) : Boolean{
+		// A variable that indicates whether the call below succeeded
+		_root._succeeded = null;
+		
+		// Callbacks are registered with the JavaScript side as follows.
+		// On the Flash side, we maintain a lookup table that associates
+		// the methodName with the actual instance and method that are
+		// associated with this method.
+		// Using fscommand, we send over the action "addCallback", with the
+		// argument being the methodName to add, such as "foobar".
+		// The JavaScript takes these values and registers the existence of
+		// this callback point.
+		
+		// precede the method name with a _ character in case it starts
+		// with a number
+		_callbacks["_" + methodName] = {_instance: instance, _method: method};
+		_callbacks[_callbacks.length] = methodName;
+		
+		// The API for ExternalInterface says we have to make sure the call
+		// succeeded; check to see if there is a value 
+		// for _succeeded, which is set by the JavaScript side
+		if(_root._succeeded == null){
+			return false;
+		}else{
+			return true;
+		}
+	}
+	
+	public static function call(methodName:String, 
+								resultsCallback:Function) : Void{
+		// FIXME: support full JSON serialization
+		
+		// First, we pack up all of the arguments to this call and set them
+		// as Flash variables, which the JavaScript side will unpack using
+		// plugin.GetVariable(). We set the number of arguments as "_numArgs",
+		// and add each argument as a variable, such as "_1", "_2", etc., starting
+		// from 0.
+		// We then execute an fscommand with the action "call" and the
+		// argument being the method name. JavaScript takes the method name,
+		// retrieves the arguments using GetVariable, executes the method,
+		// and then places the return result in a Flash variable
+		// named "_returnResult".
+		_root._numArgs = arguments.length - 2;
+		for(var i = 2; i < arguments.length; i++){
+			var argIndex = i - 2;
+			_root["_" + argIndex] = arguments[i];
+		}
+		
+		_root._returnResult = undefined;
+		fscommand("call", methodName);
+		
+		// immediately return if the caller is not waiting for return results
+		if(resultsCallback == undefined || resultsCallback == null){
+			return;
+		}
+		
+		// check at regular intervals for return results	
+		var resultsChecker = function(){
+			if(_root._returnResult != undefined){
+				clearInterval(_root._callbackID);
+				resultsCallback.call(null, _root._returnResult);
+			}
+		};	
+		_root._callbackID = setInterval(resultsChecker, 100);
+	}
+	
+	/** 
+			Called by Flash to indicate to JavaScript that we are ready to have
+			our Flash functions called. Calling loaded()
+			will fire the dojo.flash.loaded() event, so that JavaScript can know that
+			Flash has finished loading and adding its callbacks, and can begin to
+			interact with the Flash file.
+	*/
+	public static function loaded(){
+		//getURL("javascript:dojo.debug('FLASH:loaded')");
+		
+		// one more step: see if fscommands are ready to be executed; if not,
+		// set an interval that will keep running until fscommands are ready;
+		// make sure the gateway is loaded as well
+		var execLoaded = function(){
+			if(DojoExternalInterface._fscommandReady == true){
+				clearInterval(_root.loadedInterval);
+				
+				// initialize the small Flash file that helps gateway JS to Flash
+				// calls
+				DojoExternalInterface._initializeFlashRunner();
+			}	
+		};
+		
+		if(_fscommandReady == true){
+			execLoaded();
+		}else{
+			_root.loadedInterval = setInterval(execLoaded, 50);
+		}
+	}
+	
+	/** 
+			Handles and executes a JavaScript to Flash method call. Used by
+			initializeFlashRunner. 
+	*/
+	public static function _handleJSCall(){
+		// get our parameters
+		var numArgs = parseInt(_root._numArgs);
+		var jsArgs = new Array();
+		for(var i = 0; i < numArgs; i++){
+			var currentValue = _root["_" + i];
+			jsArgs.push(currentValue);
+		}
+		
+		// get our function name
+		var functionName = _root._functionName;
+		
+		// now get the actual instance and method object to execute on,
+		// using our lookup table that was constructed by calls to
+		// addCallback on initialization
+		var instance = _callbacks["_" + functionName]._instance;
+		var method = _callbacks["_" + functionName]._method;
+		
+		// execute it
+		var results = method.apply(instance, jsArgs);
+		
+		// return the results
+		_root._returnResult = results;
+	}
+	
+	/** Called by the flash6_gateway.swf to indicate that it is loaded. */
+	public static function _gatewayReady(){
+		for(var i = 0; i < _callbacks.length; i++){
+			fscommand("addCallback", _callbacks[i]);
+		}
+		call("dojo.flash.loaded");
+	}
+	
+	/** 
+			When JavaScript wants to communicate with Flash it simply sets
+			the Flash variable "_execute" to true; this method creates the
+			internal Movie Clip, called the Flash Runner, that makes this
+			magic happen.
+	*/
+	public static function _initializeFlashRunner(){
+		// figure out where our Flash movie is
+		var swfLoc = DojoExternalInterface.dojoPath + "flash6_gateway.swf";
+		
+		// load our gateway helper file
+		_root.createEmptyMovieClip("_flashRunner", 5000);
+		_root._flashRunner._lockroot = true;
+		_root._flashRunner.loadMovie(swfLoc);
+	}
+	
+	private static function getDojoPath(){
+		var url = _root._url;
+		var start = url.indexOf("baseRelativePath=") + "baseRelativePath=".length;
+		var path = url.substring(start);
+		var end = path.indexOf("&");
+		if(end != -1){
+			path = path.substring(0, end);
+		}
+		return path;
+	}
+}
+
+// vim:ts=4:noet:tw=0:


Property changes on: trunk/PhotoOp/root/static/js/src/flash/flash6/DojoExternalInterface.as
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/flash/flash6/flash6_gateway.fla
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/flash/flash6/flash6_gateway.fla
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/flash/flash8/DojoExternalInterface.as
===================================================================
--- trunk/PhotoOp/root/static/js/src/flash/flash8/DojoExternalInterface.as	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/flash/flash8/DojoExternalInterface.as	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,234 @@
+/*
+	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
+*/
+
+/**
+	A wrapper around Flash 8's ExternalInterface; DojoExternalInterface is needed so that we
+	can do a Flash 6 implementation of ExternalInterface, and be able
+	to support having a single codebase that uses DojoExternalInterface
+	across Flash versions rather than having two seperate source bases,
+	where one uses ExternalInterface and the other uses DojoExternalInterface.
+	
+	DojoExternalInterface class does a variety of optimizations to bypass ExternalInterface's
+	unbelievably bad performance so that we can have good performance
+	on Safari; see the blog post
+	http://codinginparadise.org/weblog/2006/02/how-to-speed-up-flash-8s.html
+	for details.
+	
+	@author Brad Neuberg, bkn3 at columbia.edu
+*/
+import flash.external.ExternalInterface;
+
+class DojoExternalInterface{
+	public static var available:Boolean;
+	public static var dojoPath = "";
+	
+	private static var flashMethods:Array = new Array();
+	private static var numArgs:Number;
+	private static var argData:Array;
+	private static var resultData = null;
+	
+	public static function initialize(){
+		// extract the dojo base path
+		DojoExternalInterface.dojoPath = DojoExternalInterface.getDojoPath();
+		
+		// see if we need to do an express install
+		var install:ExpressInstall = new ExpressInstall();
+		if(install.needsUpdate){
+			install.init();
+		}
+		
+		// register our callback functions
+		ExternalInterface.addCallback("startExec", DojoExternalInterface, startExec);
+		ExternalInterface.addCallback("setNumberArguments", DojoExternalInterface,
+																	setNumberArguments);
+		ExternalInterface.addCallback("chunkArgumentData", DojoExternalInterface,
+																	chunkArgumentData);
+		ExternalInterface.addCallback("exec", DojoExternalInterface, exec);
+		ExternalInterface.addCallback("getReturnLength", DojoExternalInterface,
+																	getReturnLength);
+		ExternalInterface.addCallback("chunkReturnData", DojoExternalInterface,
+																	chunkReturnData);
+		ExternalInterface.addCallback("endExec", DojoExternalInterface, endExec);
+		
+		// set whether communication is available
+		DojoExternalInterface.available = ExternalInterface.available;
+		DojoExternalInterface.call("loaded");
+	}
+	
+	public static function addCallback(methodName:String, instance:Object, 
+										 								 method:Function) : Boolean{
+		// register DojoExternalInterface methodName with it's instance
+		DojoExternalInterface.flashMethods[methodName] = instance;
+		
+		// tell JavaScript about DojoExternalInterface new method so we can create a proxy
+		ExternalInterface.call("dojo.flash.comm._addExternalInterfaceCallback", 
+													 methodName);
+													 
+		return true;
+	}
+	
+	public static function call(methodName:String,
+								resultsCallback:Function) : Void{
+		// we might have any number of optional arguments, so we have to 
+		// pass them in dynamically; strip out the results callback
+		var parameters = new Array();
+		for(var i = 0; i < arguments.length; i++){
+			if(i != 1){ // skip the callback
+				parameters.push(arguments[i]);
+			}
+		}
+		
+		var results = ExternalInterface.call.apply(ExternalInterface, parameters);
+		
+		// immediately give the results back, since ExternalInterface is
+		// synchronous
+		if(resultsCallback != null && typeof resultsCallback != "undefined"){
+			resultsCallback.call(null, results);
+		}
+	}
+	
+	/** 
+			Called by Flash to indicate to JavaScript that we are ready to have
+			our Flash functions called. Calling loaded()
+			will fire the dojo.flash.loaded() event, so that JavaScript can know that
+			Flash has finished loading and adding its callbacks, and can begin to
+			interact with the Flash file.
+	*/
+	public static function loaded(){
+		DojoExternalInterface.call("dojo.flash.loaded");
+	}
+	
+	public static function startExec():Void{
+		DojoExternalInterface.numArgs = null;
+		DojoExternalInterface.argData = null;
+		DojoExternalInterface.resultData = null;
+	}
+	
+	public static function setNumberArguments(numArgs):Void{
+		DojoExternalInterface.numArgs = numArgs;
+		DojoExternalInterface.argData = new Array(DojoExternalInterface.numArgs);
+	}
+	
+	public static function chunkArgumentData(value, argIndex:Number):Void{
+		//getURL("javascript:dojo.debug('FLASH: chunkArgumentData, value="+value+", argIndex="+argIndex+"')");
+		var currentValue = DojoExternalInterface.argData[argIndex];
+		if(currentValue == null || typeof currentValue == "undefined"){
+			DojoExternalInterface.argData[argIndex] = value;
+		}else{
+			DojoExternalInterface.argData[argIndex] += value;
+		}
+	}
+	
+	public static function exec(methodName):Void{
+		// decode all of the arguments that were passed in
+		for(var i = 0; i < DojoExternalInterface.argData.length; i++){
+			DojoExternalInterface.argData[i] = 
+				DojoExternalInterface.decodeData(DojoExternalInterface.argData[i]);
+		}
+		
+		var instance = DojoExternalInterface.flashMethods[methodName];
+		DojoExternalInterface.resultData = instance[methodName].apply(
+																			instance, DojoExternalInterface.argData);
+		// encode the result data
+		DojoExternalInterface.resultData = 
+			DojoExternalInterface.encodeData(DojoExternalInterface.resultData);
+			
+		//getURL("javascript:dojo.debug('FLASH: encoded result data="+DojoExternalInterface.resultData+"')");
+	}
+	
+	public static function getReturnLength():Number{
+	 if(DojoExternalInterface.resultData == null || 
+	 					typeof DojoExternalInterface.resultData == "undefined"){
+	 	return 0;
+	 }
+	 var segments = Math.ceil(DojoExternalInterface.resultData.length / 1024);
+	 return segments;
+	}
+	
+	public static function chunkReturnData(segment:Number):String{
+		var numSegments = DojoExternalInterface.getReturnLength();
+		var startCut = segment * 1024;
+		var endCut = segment * 1024 + 1024;
+		if(segment == (numSegments - 1)){
+			endCut = segment * 1024 + DojoExternalInterface.resultData.length;
+		}
+			
+		var piece = DojoExternalInterface.resultData.substring(startCut, endCut);
+		
+		//getURL("javascript:dojo.debug('FLASH: chunking return piece="+piece+"')");
+		
+		return piece;
+	}
+	
+	public static function endExec():Void{
+	}
+	
+	private static function decodeData(data):String{
+		// we have to use custom encodings for certain characters when passing
+		// them over; for example, passing a backslash over as //// from JavaScript
+		// to Flash doesn't work
+		data = DojoExternalInterface.replaceStr(data, "&custom_backslash;", "\\");
+		
+		data = DojoExternalInterface.replaceStr(data, "\\\'", "\'");
+		data = DojoExternalInterface.replaceStr(data, "\\\"", "\"");
+		
+		return data;
+	}
+	
+	private static function encodeData(data){
+		//getURL("javascript:dojo.debug('inside flash, data before="+data+"')");
+
+		// double encode all entity values, or they will be mis-decoded
+		// by Flash when returned
+		data = DojoExternalInterface.replaceStr(data, "&", "&amp;");
+		
+		// certain XMLish characters break Flash's wire serialization for
+		// ExternalInterface; encode these into a custom encoding, rather than
+		// the standard entity encoding, because otherwise we won't be able to
+		// differentiate between our own encoding and any entity characters
+		// that are being used in the string itself
+		data = DojoExternalInterface.replaceStr(data, '<', '&custom_lt;');
+		data = DojoExternalInterface.replaceStr(data, '>', '&custom_gt;');
+		
+		// encode control characters and JavaScript delimiters
+		data = DojoExternalInterface.replaceStr(data, "\n", "\\n");
+		data = DojoExternalInterface.replaceStr(data, "\r", "\\r");
+		data = DojoExternalInterface.replaceStr(data, "\f", "\\f");
+		data = DojoExternalInterface.replaceStr(data, "'", "\\'");
+		data = DojoExternalInterface.replaceStr(data, '"', '\"');
+		
+		//getURL("javascript:dojo.debug('inside flash, data after="+data+"')");
+		return data;
+	}
+	
+	/** 
+			Flash ActionScript has no String.replace method or support for
+			Regular Expressions! We roll our own very simple one.
+	*/
+	private static function replaceStr(inputStr:String, replaceThis:String, 
+																		 withThis:String):String {
+		var splitStr = inputStr.split(replaceThis)
+		inputStr = splitStr.join(withThis)
+		return inputStr;
+	}
+	
+	private static function getDojoPath(){
+		var url = _root._url;
+		var start = url.indexOf("baseRelativePath=") + "baseRelativePath=".length;
+		var path = url.substring(start);
+		var end = path.indexOf("&");
+		if(end != -1){
+			path = path.substring(0, end);
+		}
+		return path;
+	}
+}
+
+// vim:ts=4:noet:tw=0:


Property changes on: trunk/PhotoOp/root/static/js/src/flash/flash8/DojoExternalInterface.as
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/flash/flash8/ExpressInstall.as
===================================================================
--- trunk/PhotoOp/root/static/js/src/flash/flash8/ExpressInstall.as	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/flash/flash8/ExpressInstall.as	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,81 @@
+/*
+	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
+*/
+
+/**
+ * Based on the expressinstall.as class created by Geoff Stearns as part
+ * of the FlashObject library.
+ *
+ * Use this file to invoke the Macromedia Flash Player Express Install functionality
+ * This file is intended for use with the FlashObject embed script. You can download FlashObject 
+ * and this file at the following URL: http://blog.deconcept.com/flashobject/
+ *
+ * Usage: 
+ *          var ExpressInstall = new ExpressInstall();
+ *          
+ *          // test to see if install is needed:
+ *          if (ExpressInstall.needsUpdate) { // returns true if update is needed
+ *              ExpressInstall.init(); // starts the update
+ *          }
+ *
+ *	NOTE: Your Flash movie must be at least 214px by 137px in order to use ExpressInstall.
+ *
+ */
+
+class ExpressInstall {
+	public var needsUpdate:Boolean;
+	private var updater:MovieClip;
+	private var hold:MovieClip;
+	
+	public function ExpressInstall(){
+		// does the user need to update?
+		this.needsUpdate = (_root.MMplayerType == undefined) ? false : true;	
+	}
+
+	public function init():Void{
+		this.loadUpdater();
+	}
+
+	public function loadUpdater():Void {
+		System.security.allowDomain("fpdownload.macromedia.com");
+
+		// hope that nothing is at a depth of 10000000, you can change this depth if needed, but you want
+		// it to be on top of your content if you have any stuff on the first frame
+		this.updater = _root.createEmptyMovieClip("expressInstallHolder", 10000000);
+
+		// register the callback so we know if they cancel or there is an error
+		var _self = this;
+		this.updater.installStatus = _self.onInstallStatus;
+		this.hold = this.updater.createEmptyMovieClip("hold", 1);
+
+		// can't use movieClipLoader because it has to work in 6.0.65
+		this.updater.onEnterFrame = function():Void {
+			if(typeof this.hold.startUpdate == 'function'){
+				_self.initUpdater();
+				this.onEnterFrame = null;
+			}
+		}
+
+		var cacheBuster:Number = Math.random();
+
+		this.hold.loadMovie("http://fpdownload.macromedia.com/pub/flashplayer/"
+												+"update/current/swf/autoUpdater.swf?"+ cacheBuster);
+	}
+
+	private function initUpdater():Void{
+		this.hold.redirectURL = _root.MMredirectURL;
+		this.hold.MMplayerType = _root.MMplayerType;
+		this.hold.MMdoctitle = _root.MMdoctitle;
+		this.hold.startUpdate();
+	}
+
+	public function onInstallStatus(msg):Void{
+		getURL("javascript:dojo.flash.install._onInstallStatus('"+msg+"')");
+	}
+}


Property changes on: trunk/PhotoOp/root/static/js/src/flash/flash8/ExpressInstall.as
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/flash.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/flash.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/flash.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,1244 @@
+/*
+	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.flash");
+
+dojo.require("dojo.string.*");
+dojo.require("dojo.uri.*");
+
+
+/** 
+		The goal of dojo.flash is to make it easy to extend Flash's capabilities
+		into an AJAX/DHTML environment. Robust, performant, reliable 
+		JavaScript/Flash communication is harder than most realize when they
+		delve into the topic, especially if you want it
+		to work on Internet Explorer, Firefox, and Safari, and to be able to
+		push around hundreds of K of information quickly. Dojo.flash makes it
+		possible to support these platforms; you have to jump through a few
+		hoops to get its capabilites, but if you are a library writer 
+		who wants to bring Flash's storage or streaming sockets ability into
+		DHTML, for example, then dojo.flash is perfect for you.
+  
+		Dojo.flash provides an easy object for interacting with the Flash plugin. 
+		This object provides methods to determine the current version of the Flash
+		plugin (dojo.flash.info); execute Flash instance methods 
+		independent of the Flash version
+		being used (dojo.flash.comm); write out the necessary markup to 
+		dynamically insert a Flash object into the page (dojo.flash.Embed; and 
+		do dynamic installation and upgrading of the current Flash plugin in 
+		use (dojo.flash.Install).
+		
+		To use dojo.flash, you must first wait until Flash is finished loading 
+		and initializing before you attempt communication or interaction. 
+		To know when Flash is finished use dojo.event.connect:
+		
+		dojo.event.connect(dojo.flash, "loaded", myInstance, "myCallback");
+		
+		Then, while the page is still loading provide the file name
+		and the major version of Flash that will be used for Flash/JavaScript
+		communication (see "Flash Communication" below for information on the 
+		different kinds of Flash/JavaScript communication supported and how they 
+		depend on the version of Flash installed):
+		
+		dojo.flash.setSwf({flash6: "src/storage/storage_flash6.swf",
+											 flash8: "src/storage/storage_flash8.swf"});
+		
+		This will cause dojo.flash to pick the best way of communicating
+		between Flash and JavaScript based on the platform.
+		
+		If no SWF files are specified, then Flash is not initialized.
+		
+		Your Flash must use DojoExternalInterface to expose Flash methods and
+		to call JavaScript; see "Flash Communication" below for details.
+		
+		setSwf can take an optional 'visible' attribute to control whether
+		the Flash object is visible or not on the page; the default is visible:
+		
+		dojo.flash.setSwf({flash6: "src/storage/storage_flash6.swf",
+											 flash8: "src/storage/storage_flash8.swf",
+											 visible: false});
+		
+		Once finished, you can query Flash version information:
+		
+		dojo.flash.info.version
+		
+		Or can communicate with Flash methods that were exposed:
+		
+		var results = dojo.flash.comm.sayHello("Some Message");
+		
+		Only string values are currently supported for both arguments and
+		for return results. Everything will be cast to a string on both
+		the JavaScript and Flash sides.
+		
+		-------------------
+		Flash Communication
+		-------------------
+		
+		dojo.flash allows Flash/JavaScript communication in 
+		a way that can pass large amounts of data back and forth reliably and
+		very fast. The dojo.flash
+		framework encapsulates the specific way in which this communication occurs,
+		presenting a common interface to JavaScript irrespective of the underlying
+		Flash version.
+		
+		There are currently three major ways to do Flash/JavaScript communication
+		in the Flash community:
+		
+		1) Flash 6+ - Uses Flash methods, such as SetVariable and TCallLabel,
+		and the fscommand handler to do communication. Strengths: Very fast,
+		mature, and can send extremely large amounts of data; can do
+		synchronous method calls. Problems: Does not work on Safari; works on 
+		Firefox/Mac OS X only if Flash 8 plugin is installed; cryptic to work with.
+		
+		2) Flash 8+ - Uses ExternalInterface, which provides a way for Flash
+		methods to register themselves for callbacks from JavaScript, and a way
+		for Flash to call JavaScript. Strengths: Works on Safari; elegant to
+		work with; can do synchronous method calls. Problems: Extremely buggy 
+		(fails if there are new lines in the data, for example); performance
+		degrades drastically in O(n^2) time as data grows; locks up the browser while
+		it is communicating; does not work in Internet Explorer if Flash
+		object is dynamically added to page with document.writeln, DOM methods,
+		or innerHTML.
+		
+		3) Flash 6+ - Uses two seperate Flash applets, one that we 
+		create over and over, passing input data into it using the PARAM tag, 
+		which then uses a Flash LocalConnection to pass the data to the main Flash
+		applet; communication back to Flash is accomplished using a getURL
+		call with a javascript protocol handler, such as "javascript:myMethod()".
+		Strengths: the most cross browser, cross platform pre-Flash 8 method
+		of Flash communication known; works on Safari. Problems: Timing issues;
+		clunky and complicated; slow; can only send very small amounts of
+		data (several K); all method calls are asynchronous.
+		
+		dojo.flash.comm uses only the first two methods. This framework
+		was created primarily for dojo.storage, which needs to pass very large
+		amounts of data synchronously and reliably across the Flash/JavaScript
+		boundary. We use the first method, the Flash 6 method, on all platforms
+		that support it, while using the Flash 8 ExternalInterface method
+		only on Safari with some special code to help correct ExternalInterface's
+		bugs.
+		
+		Since dojo.flash needs to have two versions of the Flash
+		file it wants to generate, a Flash 6 and a Flash 8 version to gain
+		true cross-browser compatibility, several tools are provided to ease
+		development on the Flash side.
+		
+		In your Flash file, if you want to expose Flash methods that can be
+		called, use the DojoExternalInterface class to register methods. This
+		class is an exact API clone of the standard ExternalInterface class, but
+		can work in Flash 6+ browsers. Under the covers it uses the best
+		mechanism to do communication:
+		
+		class HelloWorld{
+			function HelloWorld(){
+				// Initialize the DojoExternalInterface class
+				DojoExternalInterface.initialize();
+				
+				// Expose your methods
+				DojoExternalInterface.addCallback("sayHello", this, this.sayHello);
+				
+				// Tell JavaScript that you are ready to have method calls
+				DojoExternalInterface.loaded();
+				
+				// Call some JavaScript
+				var resultsReady = function(results){
+					trace("Received the following results from JavaScript: " + results);
+				}
+				DojoExternalInterface.call("someJavaScriptMethod", resultsReady, 
+																	 someParameter);
+			}
+			
+			function sayHello(){ ... }
+			
+			static main(){ ... }
+		}
+		
+		DojoExternalInterface adds two new functions to the ExternalInterface
+		API: initialize() and loaded(). initialize() must be called before
+		any addCallback() or call() methods are run, and loaded() must be
+		called after you are finished adding your callbacks. Calling loaded()
+		will fire the dojo.flash.loaded() event, so that JavaScript can know that
+		Flash has finished loading and adding its callbacks, and can begin to
+		interact with the Flash file.
+		
+		To generate your SWF files, use the ant task
+		"buildFlash". You must have the open source Motion Twin ActionScript 
+		compiler (mtasc) installed and in your path to use the "buildFlash"
+		ant task; download and install mtasc from http://www.mtasc.org/.
+		
+		
+		
+		buildFlash usage:
+		
+		ant buildFlash -Ddojo.flash.file=../tests/flash/HelloWorld.as
+		
+		where "dojo.flash.file" is the relative path to your Flash 
+		ActionScript file.
+		
+		This will generate two SWF files, one ending in _flash6.swf and the other
+		ending in _flash8.swf in the same directory as your ActionScript method:
+		
+		HelloWorld_flash6.swf
+		HelloWorld_flash8.swf
+		
+		Initialize dojo.flash with the filename and Flash communication version to
+		use during page load; see the documentation for dojo.flash for details:
+		
+		dojo.flash.setSwf({flash6: "tests/flash/HelloWorld_flash6.swf",
+											 flash8: "tests/flash/HelloWorld_flash8.swf"});
+		
+		Now, your Flash methods can be called from JavaScript as if they are native
+		Flash methods, mirrored exactly on the JavaScript side:
+		
+		dojo.flash.comm.sayHello();
+		
+		Only Strings are supported being passed back and forth currently.
+		
+		JavaScript to Flash communication is synchronous; i.e., results are returned
+		directly from the method call:
+		
+		var results = dojo.flash.comm.sayHello();
+		
+		Flash to JavaScript communication is asynchronous due to limitations in
+		the underlying technologies; you must use a results callback to handle
+		results returned by JavaScript in your Flash AS files:
+		
+		var resultsReady = function(results){
+			trace("Received the following results from JavaScript: " + results);
+		}
+		DojoExternalInterface.call("someJavaScriptMethod", resultsReady);
+		
+		
+		
+		-------------------
+		Notes
+		-------------------
+		
+		If you have both Flash 6 and Flash 8 versions of your file:
+		
+		dojo.flash.setSwf({flash6: "tests/flash/HelloWorld_flash6.swf",
+											 flash8: "tests/flash/HelloWorld_flash8.swf"});
+											 
+		but want to force the browser to use a certain version of Flash for
+		all platforms (for testing, for example), use the djConfig
+		variable 'forceFlashComm' with the version number to force:
+		
+		var djConfig = { forceFlashComm: 6 };
+		
+		Two values are currently supported, 6 and 8, for the two styles of
+		communication described above. Just because you force dojo.flash
+		to use a particular communication style is no guarantee that it will
+		work; for example, Flash 8 communication doesn't work in Internet
+		Explorer due to bugs in Flash, and Flash 6 communication does not work
+		in Safari. It is best to let dojo.flash determine the best communication
+		mechanism, and to use the value above only for debugging the dojo.flash
+		framework itself.
+		
+		Also note that dojo.flash can currently only work with one Flash object
+		on the page; it and the API do not yet support multiple Flash objects on
+		the same page.
+		
+		We use some special tricks to get decent, linear performance
+		out of Flash 8's ExternalInterface on Safari; see the blog
+		post 
+		http://codinginparadise.org/weblog/2006/02/how-to-speed-up-flash-8s.html
+		for details.
+		
+		Your code can detect whether the Flash player is installing or having
+		its version revved in two ways. First, if dojo.flash detects that
+		Flash installation needs to occur, it sets dojo.flash.info.installing
+		to true. Second, you can detect if installation is necessary with the
+		following callback:
+		
+		dojo.event.connect(dojo.flash, "installing", myInstance, "myCallback");
+		
+		You can use this callback to delay further actions that might need Flash;
+		when installation is finished the full page will be refreshed and the
+		user will be placed back on your page with Flash installed.
+		
+		Two utility methods exist if you want to add loading and installing
+		listeners without creating dependencies on dojo.event; these are
+		'addLoadingListener' and 'addInstallingListener'.
+		
+		-------------------
+		Todo/Known Issues
+		-------------------
+
+		There are several tasks I was not able to do, or did not need to fix
+		to get dojo.storage out:		
+		
+		* When using Flash 8 communication, Flash method calls to JavaScript
+		are not working properly; serialization might also be broken for certain
+		invalid characters when it is Flash invoking JavaScript methods.
+		The Flash side needs to have more sophisticated serialization/
+		deserialization mechanisms like JavaScript currently has. The
+		test_flash2.html unit tests should also be updated to have much more
+		sophisticated Flash to JavaScript unit tests, including large
+		amounts of data.
+		
+		* On Internet Explorer, after doing a basic install, the page is
+		not refreshed or does not detect that Flash is now available. The way
+		to fix this is to create a custom small Flash file that is pointed to
+		during installation; when it is finished loading, it does a callback
+		that says that Flash installation is complete on IE, and we can proceed
+		to initialize the dojo.flash subsystem.
+		
+		@author Brad Neuberg, bkn3 at columbia.edu
+*/
+
+dojo.flash = {
+	flash6_version: null,
+	flash8_version: null,
+	ready: false,
+	_visible: true,
+	_loadedListeners: new Array(),
+	_installingListeners: new Array(),
+	
+	/** Sets the SWF files and versions we are using. */
+	setSwf: function(fileInfo){
+		//dojo.debug("setSwf");
+		if(fileInfo == null || dojo.lang.isUndefined(fileInfo)){
+			return;
+		}
+		
+		if(fileInfo.flash6 != null && !dojo.lang.isUndefined(fileInfo.flash6)){
+			this.flash6_version = fileInfo.flash6;
+		}
+		
+		if(fileInfo.flash8 != null && !dojo.lang.isUndefined(fileInfo.flash8)){
+			this.flash8_version = fileInfo.flash8;
+		}
+		
+		if(!dojo.lang.isUndefined(fileInfo.visible)){
+			this._visible = fileInfo.visible;
+		}
+		
+		// initialize ourselves		
+		this._initialize();
+	},
+	
+	/** Returns whether we are using Flash 6 for communication on this platform. */
+	useFlash6: function(){
+		if(this.flash6_version == null){
+			return false;
+		}else if (this.flash6_version != null && dojo.flash.info.commVersion == 6){
+			// if we have a flash 6 version of this SWF, and this browser supports 
+			// communicating using Flash 6 features...
+			return true;
+		}else{
+			return false;
+		}
+	},
+	
+	/** Returns whether we are using Flash 8 for communication on this platform. */
+	useFlash8: function(){
+		if(this.flash8_version == null){
+			return false;
+		}else if (this.flash8_version != null && dojo.flash.info.commVersion == 8){
+			// if we have a flash 8 version of this SWF, and this browser supports
+			// communicating using Flash 8 features...
+			return true;
+		}else{
+			return false;
+		}
+	},
+	
+	/** Adds a listener to know when Flash is finished loading. 
+			Useful if you don't want a dependency on dojo.event. */
+	addLoadedListener: function(listener){
+		this._loadedListeners.push(listener);
+	},
+
+	/** Adds a listener to know if Flash is being installed. 
+			Useful if you don't want a dependency on dojo.event. */
+	addInstallingListener: function(listener){
+		this._installingListeners.push(listener);
+	},	
+	
+	/** 
+			A callback when the Flash subsystem is finished loading and can be
+			worked with. To be notified when Flash is finished loading, connect
+			your callback to this method using the following:
+			
+			dojo.event.connect(dojo.flash, "loaded", myInstance, "myCallback");
+	*/
+	loaded: function(){
+		//dojo.debug("dojo.flash.loaded");
+		dojo.flash.ready = true;
+		if(dojo.flash._loadedListeners.length > 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('<script language="VBScript" type="text/vbscript"\>');
+		document.writeln('Function VBGetSwfVer(i)');
+		document.writeln('  on error resume next');
+		document.writeln('  Dim swControl, swVersion');
+		document.writeln('  swVersion = 0');
+		document.writeln('  set swControl = CreateObject("ShockwaveFlash.ShockwaveFlash." + CStr(i))');
+		document.writeln('  if (IsObject(swControl)) then');
+		document.writeln('    swVersion = swControl.GetVariable("$version")');
+		document.writeln('  end if');
+		document.writeln('  VBGetSwfVer = swVersion');
+		document.writeln('End Function');
+		document.writeln('</script\>');
+	}
+	
+	this._detectVersion();
+	this._detectCommunicationVersion();
+}
+
+dojo.flash.Info.prototype = {
+	/** The full version string, such as "8r22". */
+	version: -1,
+	
+	/** 
+			The major, minor, and revisions of the plugin. For example, if the
+			plugin is 8r22, then the major version is 8, the minor version is 0,
+			and the revision is 22. 
+	*/
+	versionMajor: -1,
+	versionMinor: -1,
+	versionRevision: -1,
+	
+	/** Whether this platform has Flash already installed. */
+	capable: false,
+	
+	/** 
+			The major version number for how our Flash and JavaScript communicate.
+			This can currently be the following values:
+			6 - We use a combination of the Flash plugin methods, such as SetVariable
+			and TCallLabel, along with fscommands, to do communication.
+			8 - We use the ExternalInterface API. 
+			-1 - For some reason neither method is supported, and no communication
+			is possible. 
+	*/
+	commVersion: 6,
+	
+	/** Set if we are in the middle of a Flash installation session. */
+	installing: false,
+	
+	/** 
+			Asserts that this environment has the given major, minor, and revision
+			numbers for the Flash player. Returns true if the player is equal
+			or above the given version, false otherwise.
+			
+			Example: To test for Flash Player 7r14:
+			
+			dojo.flash.info.isVersionOrAbove(7, 0, 14)
+	*/
+	isVersionOrAbove: function(reqMajorVer, reqMinorVer, reqVer){
+		// make the revision a decimal (i.e. transform revision 14 into
+		// 0.14
+		reqVer = parseFloat("." + reqVer);
+		
+		if(this.versionMajor >= reqMajorVer && this.versionMinor >= reqMinorVer
+			 && this.versionRevision >= reqVer){
+			return true;
+		}else{
+			return false;
+		}
+	},
+	
+	_detectVersion: function(){
+		var versionStr;
+		
+		// loop backwards through the versions until we find the newest version	
+		for(var testVersion = 25; testVersion > 0; testVersion--){
+			if(dojo.render.html.ie){
+				versionStr = VBGetSwfVer(testVersion);
+			}else{
+				versionStr = this._JSFlashInfo(testVersion);		
+			}
+				
+			if(versionStr == -1 ){
+				this.capable = false; 
+				return;
+			}else if(versionStr != 0){
+				var versionArray;
+				if(dojo.render.html.ie){
+					var tempArray = versionStr.split(" ");
+					var tempString = tempArray[1];
+					versionArray = tempString.split(",");
+				}else{
+					versionArray = versionStr.split(".");
+				}
+					
+				this.versionMajor = versionArray[0];
+				this.versionMinor = versionArray[1];
+				this.versionRevision = versionArray[2];
+				
+				// 7.0r24 == 7.24
+				var versionString = this.versionMajor + "." + this.versionRevision;
+				this.version = parseFloat(versionString);
+				
+				this.capable = true;
+				
+				break;
+			}
+		}
+	},
+	
+	/** 
+			JavaScript helper required to detect Flash Player PlugIn version 
+			information. Internet Explorer uses a corresponding Visual Basic
+			version to interact with the Flash ActiveX control. 
+	*/
+	_JSFlashInfo: function(testVersion){
+		// NS/Opera version >= 3 check for Flash plugin in plugin array
+		if(navigator.plugins != null && navigator.plugins.length > 0){
+			if(navigator.plugins["Shockwave Flash 2.0"] || 
+				 navigator.plugins["Shockwave Flash"]){
+				var swVer2 = navigator.plugins["Shockwave Flash 2.0"] ? " 2.0" : "";
+				var flashDescription = navigator.plugins["Shockwave Flash" + swVer2].description;
+				var descArray = flashDescription.split(" ");
+				var tempArrayMajor = descArray[2].split(".");
+				var versionMajor = tempArrayMajor[0];
+				var versionMinor = tempArrayMajor[1];
+				if(descArray[3] != ""){
+					var tempArrayMinor = descArray[3].split("r");
+				}else{
+					var tempArrayMinor = descArray[4].split("r");
+				}
+				var versionRevision = tempArrayMinor[1] > 0 ? tempArrayMinor[1] : 0;
+				var version = versionMajor + "." + versionMinor + "." 
+											+ versionRevision;
+											
+				return version;
+			}
+		}
+		
+		return -1;
+	},
+	
+	/** 
+			Detects the mechanisms that should be used for Flash/JavaScript 
+			communication, setting 'commVersion' to either 6 or 8. If the value is
+			6, we use Flash Plugin 6+ features, such as GetVariable, TCallLabel,
+			and fscommand, to do Flash/JavaScript communication; if the value is
+			8, we use the ExternalInterface API for communication. 
+	*/
+	_detectCommunicationVersion: function(){
+		if(this.capable == false){
+			this.commVersion = null;
+			return;
+		}
+		
+		// detect if the user has over-ridden the default flash version
+		if (typeof djConfig["forceFlashComm"] != "undefined" &&
+				typeof djConfig["forceFlashComm"] != null){
+			this.commVersion = djConfig["forceFlashComm"];
+			return;
+		}
+		
+		// we prefer Flash 6 features over Flash 8, because they are much faster
+		// and much less buggy
+		
+		// at this point, we don't have a flash file to detect features on,
+		// so we need to instead look at the browser environment we are in
+		if(dojo.render.html.safari == true || dojo.render.html.opera == true){
+			this.commVersion = 8;
+		}else{
+			this.commVersion = 6;
+		}
+	}
+};
+
+/** A class that is used to write out the Flash object into the page. */
+dojo.flash.Embed = function(visible){
+	this._visible = visible;
+}
+
+dojo.flash.Embed.prototype = {
+	/** 
+			The width of this Flash applet. The default is the minimal width
+			necessary to show the Flash settings dialog. 
+	*/
+	width: 215,
+	
+	/** 
+			The height of this Flash applet. The default is the minimal height
+			necessary to show the Flash settings dialog. 
+	*/
+	height: 138,
+	
+	/** The id of the Flash object. */
+	id: "flashObject",
+	
+	/** Controls whether this is a visible Flash applet or not. */
+	_visible: true,
+			
+	/** 
+			Writes the Flash into the page. This must be called before the page
+			is finished loading. 
+			@param flashVer The Flash version to write.
+			@param doExpressInstall Whether to write out Express Install
+			information. Optional value; defaults to false.
+	*/
+	write: function(flashVer, doExpressInstall){
+		//dojo.debug("write");
+		if(dojo.lang.isUndefined(doExpressInstall)){
+			doExpressInstall = false;
+		}
+		
+		// determine our container div's styling
+		var containerStyle = new dojo.string.Builder();
+		containerStyle.append("width: " + this.width + "px; ");
+		containerStyle.append("height: " + this.height + "px; ");
+		if(this._visible == false){
+			containerStyle.append("position: absolute; ");
+			containerStyle.append("z-index: 10000; ");
+			containerStyle.append("top: -1000px; ");
+			containerStyle.append("left: -1000px; ");
+		}
+		containerStyle = containerStyle.toString();
+
+		// figure out the SWF file to get and how to write out the correct HTML
+		// for this Flash version
+		var objectHTML;
+		var swfloc;
+		// Flash 6
+		if(flashVer == 6){
+			swfloc = dojo.flash.flash6_version;
+			var dojoPath = djConfig.baseRelativePath;
+			swfloc = swfloc + "?baseRelativePath=" + escape(dojoPath);
+			
+			objectHTML = 
+						  '<embed id="' + this.id + '" src="' + swfloc + '" '
+						+ '    quality="high" bgcolor="#ffffff" '
+						+ '    width="' + this.width + '" height="' + this.height + '" '
+						+ '    name="' + this.id + '" '
+						+ '    align="middle" allowScriptAccess="sameDomain" '
+						+ '    type="application/x-shockwave-flash" swLiveConnect="true" '
+						+ '    pluginspage="http://www.macromedia.com/go/getflashplayer">';
+		}else{ // Flash 8
+			swfloc = dojo.flash.flash8_version;
+			var swflocObject = swfloc;
+			var swflocEmbed = swfloc;
+			var dojoPath = djConfig.baseRelativePath;
+			if(doExpressInstall){
+				// the location to redirect to after installing
+				var redirectURL = escape(window.location);
+				document.title = document.title.slice(0, 47) + " - Flash Player Installation";
+				var docTitle = escape(document.title);
+				swflocObject += "?MMredirectURL=" + redirectURL
+				                + "&MMplayerType=ActiveX"
+				                + "&MMdoctitle=" + docTitle
+								+ "&baseRelativePath=" + escape(dojoPath);
+				swflocEmbed += "?MMredirectURL=" + redirectURL 
+								+ "&MMplayerType=PlugIn"
+								+ "&baseRelativePath=" + escape(dojoPath);
+			}
+			
+			objectHTML =
+				'<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" '
+				  + 'codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=8,0,0,0" '
+				  + 'width="' + this.width + '" '
+				  + 'height="' + this.height + '" '
+				  + 'id="' + this.id + '" '
+				  + 'align="middle"> '
+				  + '<param name="allowScriptAccess" value="sameDomain" /> '
+				  + '<param name="movie" value="' + swflocObject + '" /> '
+				  + '<param name="quality" value="high" /> '
+				  + '<param name="bgcolor" value="#ffffff" /> '
+				  + '<embed src="' + swflocEmbed + '" '
+				  + 'quality="high" '
+				  + 'bgcolor="#ffffff" '
+				  + 'width="' + this.width + '" '
+				  + 'height="' + this.height + '" '
+				  + 'id="' + this.id + '" '
+				  + 'name="' + this.id + '" '
+				  + 'swLiveConnect="true" '
+				  + 'align="middle" '
+				  + 'allowScriptAccess="sameDomain" '
+				  + 'type="application/x-shockwave-flash" '+ "&baseRelativePath=" + escape(dojoPath);
+				  + 'pluginspage="http://www.macromedia.com/go/getflashplayer" />'
+				+ '</object>';
+		}
+
+		// now write everything out
+		objectHTML = '<div id="' + this.id + 'Container" style="' + containerStyle + '"> '
+						+ objectHTML
+					 + '</div>';
+		document.writeln(objectHTML);
+	},  
+	
+	/** Gets the Flash object DOM node. */
+	get: function(){
+		//return (dojo.render.html.ie) ? window[this.id] : document[this.id];
+		
+		// more robust way to get Flash object; version above can break
+		// communication on IE sometimes
+		return document.getElementById(this.id);
+	},
+	
+	/** Sets the visibility of this Flash object. */
+	setVisible: function(visible){
+		var container = dojo.byId(this.id + "Container");
+		if(visible == true){
+			container.style.visibility = "visible";
+		}else{
+			container.style.position = "absolute";
+			container.style.x = "-1000px";
+			container.style.y = "-1000px";
+			container.style.visibility = "hidden";
+		}
+	},
+	
+	/** Centers the flash applet on the page. */
+	center: function(){
+		// FIXME: replace this with Dojo's centering code rather than our own
+		// We want to center the Flash applet vertically and horizontally
+		var elementWidth = this.width;
+		var elementHeight = this.height;
+    
+		// get the browser width and height; the code below
+		// works in IE and Firefox in compatibility, non-strict
+		// mode
+		var browserWidth = document.body.clientWidth;
+		var browserHeight = document.body.clientHeight;
+    
+		// in Firefox if we are in standards compliant mode
+		// (with a strict doctype), then the browser width
+		// and height have to be computed from the root level
+		// HTML element not the BODY element
+		if(!dojo.render.html.ie && document.compatMode == "CSS1Compat"){
+			browserWidth = document.body.parentNode.clientWidth;
+			browserHeight = document.body.parentNode.clientHeight;
+		}else if(dojo.render.html.ie && document.compatMode == "CSS1Compat"){
+			// IE 6 in standards compliant mode has to be calculated
+			// differently
+			browserWidth = document.documentElement.clientWidth;
+			browserHeight = document.documentElement.clientHeight;
+		}else if(dojo.render.html.safari){ // Safari works different
+			browserHeight = self.innerHeight;
+		}
+    
+		// get where we are scrolled to in the document
+		// the code below works in FireFox
+		var scrolledByWidth = window.scrollX;
+		var scrolledByHeight = window.scrollY;
+		// compute these values differently for IE;
+		// IE has two possibilities; it is either in standards
+		// compatibility mode or it is not
+		if(typeof scrolledByWidth == "undefined"){
+			if(document.compatMode == "CSS1Compat"){ // standards mode
+				scrolledByWidth = document.documentElement.scrollLeft;
+				scrolledByHeight = document.documentElement.scrollTop;
+			}else{ // Pre IE6 non-standards mode
+				scrolledByWidth = document.body.scrollLeft;
+				scrolledByHeight = document.body.scrollTop;
+			}
+		}
+
+		// compute the centered position    
+		var x = scrolledByWidth + (browserWidth - elementWidth) / 2;
+		var y = scrolledByHeight + (browserHeight - elementHeight) / 2; 
+
+		// set the centered position
+		var container = dojo.byId(this.id + "Container");
+		container.style.top = y + "px";
+		container.style.left = x + "px";
+	}
+};
+
+
+/** 
+		A class that is used to communicate between Flash and JavaScript in 
+		a way that can pass large amounts of data back and forth reliably,
+		very fast, and with synchronous method calls. This class encapsulates the 
+		specific way in which this communication occurs,
+		presenting a common interface to JavaScript irrespective of the underlying
+		Flash version.
+*/
+dojo.flash.Communicator = function(){
+	if(dojo.flash.useFlash6()){
+		this._writeFlash6();
+	}else if (dojo.flash.useFlash8()){
+		this._writeFlash8();
+	}
+}
+
+dojo.flash.Communicator.prototype = {
+	_writeFlash6: function(){
+		var id = dojo.flash.obj.id;
+		
+		// global function needed for Flash 6 callback;
+		// we write it out as a script tag because the VBScript hook for IE
+		// callbacks does not work properly if this function is evalled() from
+		// within the Dojo system
+		document.writeln('<script language="JavaScript">');
+		document.writeln('  function ' + id + '_DoFSCommand(command, args){ ');
+		document.writeln('    dojo.flash.comm._handleFSCommand(command, args); ');
+		document.writeln('}');
+		document.writeln('</script>');
+		
+		// hook for Internet Explorer to receive FSCommands from Flash
+		if(dojo.render.html.ie){
+			document.writeln('<SCRIPT LANGUAGE=VBScript\> ');
+			document.writeln('on error resume next ');
+			document.writeln('Sub ' + id + '_FSCommand(ByVal command, ByVal args)');
+			document.writeln(' call ' + id + '_DoFSCommand(command, args)');
+			document.writeln('end sub');
+			document.writeln('</SCRIPT\> ');
+		}
+	},
+	
+	_writeFlash8: function(){
+		// nothing needs to be written out for Flash 8 communication; 
+		// happens automatically
+	},
+	
+	/** Flash 6 communication. */
+	
+	/** Handles fscommand's from Flash to JavaScript. Flash 6 communication. */
+	_handleFSCommand: function(command, args){
+		//dojo.debug("fscommand, command="+command+", args="+args);
+		// Flash 8 on Mac/Firefox precedes all commands with the string "FSCommand:";
+		// strip it off if it is present
+		if(command != null && !dojo.lang.isUndefined(command)
+			&& /^FSCommand:(.*)/.test(command) == true){
+			command = command.match(/^FSCommand:(.*)/)[1];
+		}
+		 
+		if(command == "addCallback"){ // add Flash method for JavaScript callback
+			this._fscommandAddCallback(command, args);
+		}else if(command == "call"){ // Flash to JavaScript method call
+			this._fscommandCall(command, args);
+		}else if(command == "fscommandReady"){ // see if fscommands are ready
+			this._fscommandReady();
+		}
+	},
+	
+	/** Handles registering a callable Flash function. Flash 6 communication. */
+	_fscommandAddCallback: function(command, args){
+		var functionName = args;
+			
+		// do a trick, where we link this function name to our wrapper
+		// function, _call, that does the actual JavaScript to Flash call
+		var callFunc = function(){
+			return dojo.flash.comm._call(functionName, arguments);
+		};			
+		dojo.flash.comm[functionName] = callFunc;
+		
+		// indicate that the call was successful
+		dojo.flash.obj.get().SetVariable("_succeeded", true);
+	},
+	
+	/** Handles Flash calling a JavaScript function. Flash 6 communication. */
+	_fscommandCall: function(command, args){
+		var plugin = dojo.flash.obj.get();
+		var functionName = args;
+		
+		// get the number of arguments to this method call and build them up
+		var numArgs = parseInt(plugin.GetVariable("_numArgs"));
+		var flashArgs = new Array();
+		for(var i = 0; i < numArgs; i++){
+			var currentArg = plugin.GetVariable("_" + i);
+			flashArgs.push(currentArg);
+		}
+		
+		// get the function instance; we technically support more capabilities
+		// than ExternalInterface, which can only call global functions; if
+		// the method name has a dot in it, such as "dojo.flash.loaded", we
+		// eval it so that the method gets run against an instance
+		var runMe;
+		if(functionName.indexOf(".") == -1){ // global function
+			runMe = window[functionName];
+		}else{
+			// instance function
+			runMe = eval(functionName);
+		}
+		
+		// make the call and get the results
+		var results = null;
+		if(!dojo.lang.isUndefined(runMe) && runMe != null){
+			results = runMe.apply(null, flashArgs);
+		}
+		
+		// return the results to flash
+		plugin.SetVariable("_returnResult", results);
+	},
+	
+	/** Reports that fscommands are ready to run if executed from Flash. */
+	_fscommandReady: function(){
+		var plugin = dojo.flash.obj.get();
+		plugin.SetVariable("fscommandReady", "true");
+	},
+	
+	/** 
+			The actual function that will execute a JavaScript to Flash call; used
+			by the Flash 6 communication method. 
+	*/
+	_call: function(functionName, args){
+		// we do JavaScript to Flash method calls by setting a Flash variable
+		// "_functionName" with the function name; "_numArgs" with the number
+		// of arguments; and "_0", "_1", etc for each numbered argument. Flash
+		// reads these, executes the function call, and returns the result
+		// in "_returnResult"
+		var plugin = dojo.flash.obj.get();
+		plugin.SetVariable("_functionName", functionName);
+		plugin.SetVariable("_numArgs", args.length);
+		for(var i = 0; i < args.length; i++){
+			// unlike Flash 8's ExternalInterface, Flash 6 has no problem with
+			// any special characters _except_ for the null character \0; double
+			// encode this so the Flash side never sees it, but we can get it 
+			// back if the value comes back to JavaScript
+			var value = args[i];
+			value = value.replace(/\0/g, "\\0");
+			
+			plugin.SetVariable("_" + i, value);
+		}
+		
+		// now tell Flash to execute this method using the Flash Runner
+		plugin.TCallLabel("/_flashRunner", "execute");
+		
+		// get the results
+		var results = plugin.GetVariable("_returnResult");
+		
+		// we double encoded all null characters as //0 because Flash breaks
+		// if they are present; turn the //0 back into /0
+		results = results.replace(/\\0/g, "\0");
+		
+		return results;
+	},
+	
+	/** Flash 8 communication. */
+	
+	/** 
+			Registers the existence of a Flash method that we can call with
+			JavaScript, using Flash 8's ExternalInterface. 
+	*/
+	_addExternalInterfaceCallback: function(methodName){
+		var wrapperCall = function(){
+			// some browsers don't like us changing values in the 'arguments' array, so
+			// make a fresh copy of it
+			var methodArgs = new Array(arguments.length);
+			for(var i = 0; i < arguments.length; i++){
+				methodArgs[i] = arguments[i];
+			}
+			return dojo.flash.comm._execFlash(methodName, methodArgs);
+		};
+		
+		dojo.flash.comm[methodName] = wrapperCall;
+	},
+	
+	/** 
+			Encodes our data to get around ExternalInterface bugs.
+			Flash 8 communication.
+	*/
+	_encodeData: function(data){
+		// double encode all entity values, or they will be mis-decoded
+		// by Flash when returned
+		var entityRE = /\&([^;]*)\;/g;
+		data = data.replace(entityRE, "&amp;$1;");
+		
+		// entity encode XML-ish characters, or Flash's broken XML serializer
+		// breaks
+		data = data.replace(/</g, "&lt;");
+		data = data.replace(/>/g, "&gt;");
+		
+		// transforming \ into \\ doesn't work; just use a custom encoding
+		data = data.replace("\\", "&custom_backslash;&custom_backslash;");
+		
+		data = data.replace(/\n/g, "\\n");
+		data = data.replace(/\r/g, "\\r");
+		data = data.replace(/\f/g, "\\f");
+		data = data.replace(/\0/g, "\\0"); // null character
+		data = data.replace(/\'/g, "\\\'");
+		data = data.replace(/\"/g, '\\\"');
+		
+		return data;
+	},
+	
+	/** 
+			Decodes our data to get around ExternalInterface bugs.
+			Flash 8 communication.
+	*/
+	_decodeData: function(data){
+		if(data == null || typeof data == "undefined"){
+			return data;
+		}
+		
+		// certain XMLish characters break Flash's wire serialization for
+		// ExternalInterface; these are encoded on the 
+		// DojoExternalInterface side into a custom encoding, rather than
+		// the standard entity encoding, because otherwise we won't be able to
+		// differentiate between our own encoding and any entity characters
+		// that are being used in the string itself
+		data = data.replace(/\&custom_lt\;/g, "<");
+		data = data.replace(/\&custom_gt\;/g, ">");
+		
+		// Unfortunately, Flash returns us our String with special characters
+		// like newlines broken into seperate characters. So if \n represents
+		// a new line, Flash returns it as "\" and "n". This means the character
+		// is _not_ a newline. This forces us to eval() the string to cause
+		// escaped characters to turn into their real special character values.
+		data = eval('"' + data + '"');
+		
+		return data;
+	},
+	
+	/** 
+			Sends our method arguments over to Flash in chunks in order to
+			have ExternalInterface's performance not be O(n^2).
+			Flash 8 communication.
+	*/
+	_chunkArgumentData: function(value, argIndex){
+		var plugin = dojo.flash.obj.get();
+		
+		// cut up the string into pieces, and push over each piece one
+		// at a time
+		var numSegments = Math.ceil(value.length / 1024);
+		for(var i = 0; i < numSegments; i++){
+			var startCut = i * 1024;
+			var endCut = i * 1024 + 1024;
+			if(i == (numSegments - 1)){
+				endCut = i * 1024 + value.length;
+			}
+			
+			var piece = value.substring(startCut, endCut);
+			
+			// encode each piece seperately, rather than the entire
+			// argument data, because ocassionally a special 
+			// character, such as an entity like &foobar;, will fall between
+			// piece boundaries, and we _don't_ want to encode that value if
+			// it falls between boundaries, or else we will end up with incorrect
+			// data when we patch the pieces back together on the other side
+			piece = this._encodeData(piece);
+			
+			// directly use the underlying CallFunction method used by
+			// ExternalInterface, which is vastly faster for large strings
+			// and lets us bypass some Flash serialization bugs
+			plugin.CallFunction('<invoke name="chunkArgumentData" '
+														+ 'returntype="javascript">'
+														+ '<arguments>'
+														+ '<string>' + piece + '</string>'
+														+ '<number>' + argIndex + '</number>'
+														+ '</arguments>'
+														+ '</invoke>');
+		}
+	},
+	
+	/** 
+			Gets our method return data in chunks for better performance.
+			Flash 8 communication.
+	*/
+	_chunkReturnData: function(){
+		var plugin = dojo.flash.obj.get();
+		
+		var numSegments = plugin.getReturnLength();
+		var resultsArray = new Array();
+		for(var i = 0; i < numSegments; i++){
+			// directly use the underlying CallFunction method used by
+			// ExternalInterface, which is vastly faster for large strings
+			var piece = 
+					plugin.CallFunction('<invoke name="chunkReturnData" '
+															+ 'returntype="javascript">'
+															+ '<arguments>'
+															+ '<number>' + i + '</number>'
+															+ '</arguments>'
+															+ '</invoke>');
+															
+			// remove any leading or trailing JavaScript delimiters, which surround
+			// our String when it comes back from Flash since we bypass Flash's
+			// deserialization routines by directly calling CallFunction on the
+			// plugin
+			if(piece == '""' || piece == "''"){
+				piece = "";
+			}else{
+				piece = piece.substring(1, piece.length-1);
+			}
+		
+			resultsArray.push(piece);
+		}
+		var results = resultsArray.join("");
+		
+		return results;
+	},
+	
+	/** 
+			Executes a Flash method; called from the JavaScript wrapper proxy we
+			create on dojo.flash.comm.
+			Flash 8 communication.
+	*/
+	_execFlash: function(methodName, methodArgs){
+		var plugin = dojo.flash.obj.get();
+				
+		// begin Flash method execution
+		plugin.startExec();
+		
+		// set the number of arguments
+		plugin.setNumberArguments(methodArgs.length);
+		
+		// chunk and send over each argument
+		for(var i = 0; i < methodArgs.length; i++){
+			this._chunkArgumentData(methodArgs[i], i);
+		}
+		
+		// execute the method
+		plugin.exec(methodName);
+														
+		// get the return result
+		var results = this._chunkReturnData();
+		
+		// decode the results
+		results = this._decodeData(results);
+		
+		// reset everything
+		plugin.endExec();
+		
+		return results;
+
+	}
+}
+
+/** 
+		Figures out the best way to automatically install the Flash plugin
+		for this browser and platform. Also determines if installation or
+		revving of the current plugin is needed on this platform.
+*/
+dojo.flash.Install = function(){
+}
+
+dojo.flash.Install.prototype = {
+	/** 
+			Determines if installation or revving of the current plugin is 
+			needed. 
+	*/
+	needed: function(){
+		// do we even have flash?
+		if(dojo.flash.info.capable == false){
+			return true;
+		}
+
+		// are we on the Mac? Safari needs Flash version 8 to do Flash 8
+		// communication, while Firefox/Mac needs Flash 8 to fix bugs it has
+		// with Flash 6 communication
+		if(dojo.render.os.mac == true && !dojo.flash.info.isVersionOrAbove(8, 0, 0)){
+			return true;
+		}
+
+		// other platforms need at least Flash 6 or above
+		if(!dojo.flash.info.isVersionOrAbove(6, 0, 0)){
+			return true;
+		}
+
+		// otherwise we don't need installation
+		return false;
+	},
+
+	/** Performs installation or revving of the Flash plugin. */
+	install: function(){
+		//dojo.debug("install");
+		// indicate that we are installing
+		dojo.flash.info.installing = true;
+		dojo.flash.installing();
+		
+		if(dojo.flash.info.capable == false){ // we have no Flash at all
+			//dojo.debug("Completely new install");
+			// write out a simple Flash object to force the browser to prompt
+			// the user to install things
+			var installObj = new dojo.flash.Embed(false);
+			installObj.write(8); // write out HTML for Flash 8 version+
+		}else if(dojo.flash.info.isVersionOrAbove(6, 0, 65)){ // Express Install
+			//dojo.debug("Express install");
+			var installObj = new dojo.flash.Embed(false);
+			installObj.write(8, true); // write out HTML for Flash 8 version+
+			installObj.setVisible(true);
+			installObj.center();
+		}else{ // older Flash install than version 6r65
+			alert("This content requires a more recent version of the Macromedia "
+						+" Flash Player.");
+			window.location.href = "http://www.macromedia.com/go/getflashplayer";
+		}
+	},
+	
+	/** 
+			Called when the Express Install is either finished, failed, or was
+			rejected by the user.
+	*/
+	_onInstallStatus: function(msg){
+		if (msg == "Download.Complete"){
+			// Installation is complete.
+			dojo.flash._initialize();
+		}else if(msg == "Download.Cancelled"){
+			alert("This content requires a more recent version of the Macromedia "
+						+" Flash Player.");
+			window.location.href = "http://www.macromedia.com/go/getflashplayer";
+		}else if (msg == "Download.Failed"){
+			// The end user failed to download the installer due to a network failure
+			alert("There was an error downloading the Flash Player update. "
+						+ "Please try again later, or visit macromedia.com to download "
+						+ "the latest version of the Flash plugin.");
+		}	
+	}
+}
+
+// find out if Flash is installed
+dojo.flash.info = new dojo.flash.Info();
+
+// vim:ts=4:noet:tw=0:


Property changes on: trunk/PhotoOp/root/static/js/src/flash.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/fx/__package__.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/fx/__package__.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/fx/__package__.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -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.kwCompoundRequire({
+	browser: ["dojo.fx.html"],
+	dashboard: ["dojo.fx.html"]
+});
+dojo.provide("dojo.fx.*");


Property changes on: trunk/PhotoOp/root/static/js/src/fx/__package__.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/fx/html.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/fx/html.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/fx/html.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,573 @@
+/*
+	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.fx.html");
+
+dojo.require("dojo.style");
+dojo.require("dojo.math.curves");
+dojo.require("dojo.lang.func");
+dojo.require("dojo.animation");
+dojo.require("dojo.event.*");
+dojo.require("dojo.graphics.color");
+
+dojo.deprecated("dojo.fx.html", "use dojo.lfx.html instead", "0.4");
+
+dojo.fx.duration = 300;
+
+dojo.fx.html._makeFadeable = 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.style.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.style.getStyle(node, "width") == "auto") ){
+			node.style.width = "auto";
+		}
+	}
+}
+
+dojo.fx.html.fadeOut = function(node, duration, callback, dontPlay) {
+	return dojo.fx.html.fade(node, duration, dojo.style.getOpacity(node), 0, callback, dontPlay);
+};
+
+dojo.fx.html.fadeIn = function(node, duration, callback, dontPlay) {
+	return dojo.fx.html.fade(node, duration, dojo.style.getOpacity(node), 1, callback, dontPlay);
+};
+
+dojo.fx.html.fadeHide = function(node, duration, callback, dontPlay) {
+	node = dojo.byId(node);
+	if(!duration) { duration = 150; } // why not have a default?
+	return dojo.fx.html.fadeOut(node, duration, function(node) {
+		node.style.display = "none";
+		if(typeof callback == "function") { callback(node); }
+	});
+};
+
+dojo.fx.html.fadeShow = function(node, duration, callback, dontPlay) {
+	node = dojo.byId(node);
+	if(!duration) { duration = 150; } // why not have a default?
+	node.style.display = "block";
+	return dojo.fx.html.fade(node, duration, 0, 1, callback, dontPlay);
+};
+
+dojo.fx.html.fade = function(node, duration, startOpac, endOpac, callback, dontPlay) {
+	node = dojo.byId(node);
+	dojo.fx.html._makeFadeable(node);
+	var anim = new dojo.animation.Animation(
+		new dojo.math.curves.Line([startOpac],[endOpac]),
+		duration||dojo.fx.duration, 0);
+	dojo.event.connect(anim, "onAnimate", function(e) {
+		dojo.style.setOpacity(node, e.x);
+	});
+	if(callback) {
+		dojo.event.connect(anim, "onEnd", function(e) {
+			callback(node, anim);
+		});
+	}
+	if(!dontPlay) { anim.play(true); }
+	return anim;
+};
+
+dojo.fx.html.slideTo = function(node, duration, endCoords, callback, dontPlay) {
+	if(!dojo.lang.isNumber(duration)) {
+		var tmp = duration;
+		duration = endCoords;
+		endCoords = tmp;
+	}
+	node = dojo.byId(node);
+
+	var top = node.offsetTop;
+	var left = node.offsetLeft;
+	var pos = dojo.style.getComputedStyle(node, 'position');
+
+	if (pos == 'relative' || pos == 'static') {
+		top = parseInt(dojo.style.getComputedStyle(node, 'top')) || 0;
+		left = parseInt(dojo.style.getComputedStyle(node, 'left')) || 0;
+	}
+
+	return dojo.fx.html.slide(node, duration, [left, top],
+		endCoords, callback, dontPlay);
+};
+
+dojo.fx.html.slideBy = function(node, duration, coords, callback, dontPlay) {
+	if(!dojo.lang.isNumber(duration)) {
+		var tmp = duration;
+		duration = coords;
+		coords = tmp;
+	}
+	node = dojo.byId(node);
+
+	var top = node.offsetTop;
+	var left = node.offsetLeft;
+	var pos = dojo.style.getComputedStyle(node, 'position');
+
+	if (pos == 'relative' || pos == 'static') {
+		top = parseInt(dojo.style.getComputedStyle(node, 'top')) || 0;
+		left = parseInt(dojo.style.getComputedStyle(node, 'left')) || 0;
+	}
+
+	return dojo.fx.html.slideTo(node, duration, [left+coords[0], top+coords[1]],
+		callback, dontPlay);
+};
+
+dojo.fx.html.slide = function(node, duration, startCoords, endCoords, callback, dontPlay) {
+	if(!dojo.lang.isNumber(duration)) {
+		var tmp = duration;
+		duration = endCoords;
+		endCoords = startCoords;
+		startCoords = tmp;
+	}
+	node = dojo.byId(node);
+
+	if (dojo.style.getComputedStyle(node, 'position') == 'static') {
+		node.style.position = 'relative';
+	}
+
+	var anim = new dojo.animation.Animation(
+		new dojo.math.curves.Line(startCoords, endCoords),
+		duration||dojo.fx.duration, 0);
+	dojo.event.connect(anim, "onAnimate", function(e) {
+		with( node.style ) {
+			left = e.x + "px";
+			top = e.y + "px";
+		}
+	});
+	if(callback) {
+		dojo.event.connect(anim, "onEnd", function(e) {
+			callback(node, anim);
+		});
+	}
+	if(!dontPlay) { anim.play(true); }
+	return anim;
+};
+
+// Fade from startColor to the node's background color
+dojo.fx.html.colorFadeIn = function(node, duration, startColor, delay, callback, dontPlay) {
+	if(!dojo.lang.isNumber(duration)) {
+		var tmp = duration;
+		duration = startColor;
+		startColor = tmp;
+	}
+	node = dojo.byId(node);
+	var color = dojo.style.getBackgroundColor(node);
+	var bg = dojo.style.getStyle(node, "background-color").toLowerCase();
+	var wasTransparent = bg == "transparent" || bg == "rgba(0, 0, 0, 0)";
+	while(color.length > 3) { color.pop(); }
+
+	var rgb = new dojo.graphics.color.Color(startColor).toRgb();
+	var anim = dojo.fx.html.colorFade(node, duration||dojo.fx.duration, startColor, color, callback, true);
+	dojo.event.connect(anim, "onEnd", function(e) {
+		if( wasTransparent ) {
+			node.style.backgroundColor = "transparent";
+		}
+	});
+	if( delay > 0 ) {
+		node.style.backgroundColor = "rgb(" + rgb.join(",") + ")";
+		if(!dontPlay) { setTimeout(function(){anim.play(true)}, delay); }
+	} else {
+		if(!dontPlay) { anim.play(true); }
+	}
+	return anim;
+};
+// alias for (probably?) common use/terminology
+dojo.fx.html.highlight = dojo.fx.html.colorFadeIn;
+dojo.fx.html.colorFadeFrom = dojo.fx.html.colorFadeIn;
+
+// Fade from node's background color to endColor
+dojo.fx.html.colorFadeOut = function(node, duration, endColor, delay, callback, dontPlay) {
+	if(!dojo.lang.isNumber(duration)) {
+		var tmp = duration;
+		duration = endColor;
+		endColor = tmp;
+	}
+	node = dojo.byId(node);
+	var color = new dojo.graphics.color.Color(dojo.style.getBackgroundColor(node)).toRgb();
+
+	var rgb = new dojo.graphics.color.Color(endColor).toRgb();
+	var anim = dojo.fx.html.colorFade(node, duration||dojo.fx.duration, color, rgb, callback, delay > 0 || dontPlay);
+	if( delay > 0 ) {
+		node.style.backgroundColor = "rgb(" + color.join(",") + ")";
+		if(!dontPlay) { setTimeout(function(){anim.play(true)}, delay); }
+	}
+	return anim;
+};
+// FIXME: not sure which name is better. an alias here may be bad.
+dojo.fx.html.unhighlight = dojo.fx.html.colorFadeOut;
+dojo.fx.html.colorFadeTo = dojo.fx.html.colorFadeOut;
+
+// Fade node background from startColor to endColor
+dojo.fx.html.colorFade = function(node, duration, startColor, endColor, callback, dontPlay) {
+	if(!dojo.lang.isNumber(duration)) {
+		var tmp = duration;
+		duration = endColor;
+		endColor = startColor;
+		startColor = tmp;
+	}
+	node = dojo.byId(node);
+	var startRgb = new dojo.graphics.color.Color(startColor).toRgb();
+	var endRgb = new dojo.graphics.color.Color(endColor).toRgb();
+	var anim = new dojo.animation.Animation(
+		new dojo.math.curves.Line(startRgb, endRgb),
+		duration||dojo.fx.duration, 0);
+	dojo.event.connect(anim, "onAnimate", function(e) {
+		node.style.backgroundColor = "rgb(" + e.coordsAsInts().join(",") + ")";
+	});
+	if(callback) {
+		dojo.event.connect(anim, "onEnd", function(e) {
+			callback(node, anim);
+		});
+	}
+	if( !dontPlay ) { anim.play(true); }
+	return anim;
+};
+
+dojo.fx.html.wipeIn = function(node, duration, callback, dontPlay) {
+	node = dojo.byId(node);
+	var overflow = dojo.style.getStyle(node, "overflow");
+	if(overflow == "visible") {
+		node.style.overflow = "hidden";
+	}
+	node.style.height = 0;
+	dojo.style.show(node);
+	var anim = dojo.fx.html.wipe(node, duration, 0, node.scrollHeight, null, true);
+	dojo.event.connect(anim, "onEnd", function() {
+		node.style.overflow = overflow;
+		node.style.visibility = "";
+		node.style.height = "auto";
+		if(callback) { callback(node, anim); }
+	});
+	if(!dontPlay) { anim.play(); }
+	return anim;
+}
+
+dojo.fx.html.wipeOut = function(node, duration, callback, dontPlay) {
+	node = dojo.byId(node);
+	var overflow = dojo.style.getStyle(node, "overflow");
+	if(overflow == "visible") {
+		node.style.overflow = "hidden";
+	}
+	var anim = dojo.fx.html.wipe(node, duration, node.offsetHeight, 0, null, true);
+	dojo.event.connect(anim, "onEnd", function() {
+		dojo.style.hide(node);
+		node.style.visibility = "hidden";
+		node.style.overflow = overflow;
+		if(callback) { callback(node, anim); }
+	});
+	if(!dontPlay) { anim.play(); }
+	return anim;
+}
+
+dojo.fx.html.wipe = function(node, duration, startHeight, endHeight, callback, dontPlay) {
+	node = dojo.byId(node);
+	var anim = new dojo.animation.Animation([[startHeight], [endHeight]], duration||dojo.fx.duration, 0);
+	dojo.event.connect(anim, "onAnimate", function(e) {
+		node.style.height = e.x + "px";
+	});
+	dojo.event.connect(anim, "onEnd", function() {
+		if(callback) { callback(node, anim); }
+	});
+	if(!dontPlay) { anim.play(); }
+	return anim;
+}
+
+dojo.fx.html.wiper = function(node, controlNode) {
+	this.node = dojo.byId(node);
+	if(controlNode) {
+		dojo.event.connect(dojo.byId(controlNode), "onclick", this, "toggle");
+	}
+}
+dojo.lang.extend(dojo.fx.html.wiper, {
+	duration: dojo.fx.duration,
+	_anim: null,
+
+	toggle: function() {
+		if(!this._anim) {
+			var type = "wipe" + (dojo.style.isVisible(this.node) ? "Out" : "In");
+			this._anim = dojo.fx[type](this.node, this.duration, dojo.lang.hitch(this, "_callback"));
+		}
+	},
+
+	_callback: function() {
+		this._anim = null;
+	}
+});
+
+dojo.fx.html.explode = function(start, endNode, duration, callback, dontPlay) {
+	var startCoords = dojo.style.toCoordinateArray(start);
+
+	var outline = document.createElement("div");
+	with(outline.style) {
+		position = "absolute";
+		border = "1px solid black";
+		display = "none";
+	}
+	document.body.appendChild(outline);
+
+	endNode = dojo.byId(endNode);
+	with(endNode.style) {
+		visibility = "hidden";
+		display = "block";
+	}
+	var endCoords = dojo.style.toCoordinateArray(endNode);
+
+	with(endNode.style) {
+		display = "none";
+		visibility = "visible";
+	}
+
+	var anim = new dojo.animation.Animation(
+		new dojo.math.curves.Line(startCoords, endCoords),
+		duration||dojo.fx.duration, 0
+	);
+	dojo.event.connect(anim, "onBegin", function(e) {
+		outline.style.display = "block";
+	});
+	dojo.event.connect(anim, "onAnimate", function(e) {
+		with(outline.style) {
+			left = e.x + "px";
+			top = e.y + "px";
+			width = e.coords[2] + "px";
+			height = e.coords[3] + "px";
+		}
+	});
+
+	dojo.event.connect(anim, "onEnd", function() {
+		endNode.style.display = "block";
+		outline.parentNode.removeChild(outline);
+		if(callback) { callback(endNode, anim); }
+	});
+	if(!dontPlay) { anim.play(); }
+	return anim;
+};
+
+dojo.fx.html.implode = function(startNode, end, duration, callback, dontPlay) {
+	var startCoords = dojo.style.toCoordinateArray(startNode);
+	var endCoords = dojo.style.toCoordinateArray(end);
+
+	startNode = dojo.byId(startNode);
+	var outline = document.createElement("div");
+	with(outline.style) {
+		position = "absolute";
+		border = "1px solid black";
+		display = "none";
+	}
+	document.body.appendChild(outline);
+
+	var anim = new dojo.animation.Animation(
+		new dojo.math.curves.Line(startCoords, endCoords),
+		duration||dojo.fx.duration, 0
+	);
+	dojo.event.connect(anim, "onBegin", function(e) {
+		startNode.style.display = "none";
+		outline.style.display = "block";
+	});
+	dojo.event.connect(anim, "onAnimate", function(e) {
+		with(outline.style) {
+			left = e.x + "px";
+			top = e.y + "px";
+			width = e.coords[2] + "px";
+			height = e.coords[3] + "px";
+		}
+	});
+
+	dojo.event.connect(anim, "onEnd", function() {
+		outline.parentNode.removeChild(outline);
+		if(callback) { callback(startNode, anim); }
+	});
+	if(!dontPlay) { anim.play(); }
+	return anim;
+};
+
+dojo.fx.html.Exploder = function(triggerNode, boxNode) {
+	triggerNode = dojo.byId(triggerNode);
+	boxNode = dojo.byId(boxNode);
+	var _this = this;
+
+	// custom options
+	this.waitToHide = 500;
+	this.timeToShow = 100;
+	this.waitToShow = 200;
+	this.timeToHide = 70;
+	this.autoShow = false;
+	this.autoHide = false;
+
+	var animShow = null;
+	var animHide = null;
+
+	var showTimer = null;
+	var hideTimer = null;
+
+	var startCoords = null;
+	var endCoords = null;
+
+	this.showing = false;
+
+	this.onBeforeExplode = null;
+	this.onAfterExplode = null;
+	this.onBeforeImplode = null;
+	this.onAfterImplode = null;
+	this.onExploding = null;
+	this.onImploding = null;
+
+	this.timeShow = function() {
+		clearTimeout(showTimer);
+		showTimer = setTimeout(_this.show, _this.waitToShow);
+	}
+
+	this.show = function() {
+		clearTimeout(showTimer);
+		clearTimeout(hideTimer);
+		//triggerNode.blur();
+
+		if( (animHide && animHide.status() == "playing")
+			|| (animShow && animShow.status() == "playing")
+			|| _this.showing ) { return; }
+
+		if(typeof _this.onBeforeExplode == "function") { _this.onBeforeExplode(triggerNode, boxNode); }
+		animShow = dojo.fx.html.explode(triggerNode, boxNode, _this.timeToShow, function(e) {
+			_this.showing = true;
+			if(typeof _this.onAfterExplode == "function") { _this.onAfterExplode(triggerNode, boxNode); }
+		});
+		if(typeof _this.onExploding == "function") {
+			dojo.event.connect(animShow, "onAnimate", this, "onExploding");
+		}
+	}
+
+	this.timeHide = function() {
+		clearTimeout(showTimer);
+		clearTimeout(hideTimer);
+		if(_this.showing) {
+			hideTimer = setTimeout(_this.hide, _this.waitToHide);
+		}
+	}
+
+	this.hide = function() {
+		clearTimeout(showTimer);
+		clearTimeout(hideTimer);
+		if( animShow && animShow.status() == "playing" ) {
+			return;
+		}
+
+		_this.showing = false;
+		if(typeof _this.onBeforeImplode == "function") { _this.onBeforeImplode(triggerNode, boxNode); }
+		animHide = dojo.fx.html.implode(boxNode, triggerNode, _this.timeToHide, function(e){
+			if(typeof _this.onAfterImplode == "function") { _this.onAfterImplode(triggerNode, boxNode); }
+		});
+		if(typeof _this.onImploding == "function") {
+			dojo.event.connect(animHide, "onAnimate", this, "onImploding");
+		}
+	}
+
+	// trigger events
+	dojo.event.connect(triggerNode, "onclick", function(e) {
+		if(_this.showing) {
+			_this.hide();
+		} else {
+			_this.show();
+		}
+	});
+	dojo.event.connect(triggerNode, "onmouseover", function(e) {
+		if(_this.autoShow) {
+			_this.timeShow();
+		}
+	});
+	dojo.event.connect(triggerNode, "onmouseout", function(e) {
+		if(_this.autoHide) {
+			_this.timeHide();
+		}
+	});
+
+	// box events
+	dojo.event.connect(boxNode, "onmouseover", function(e) {
+		clearTimeout(hideTimer);
+	});
+	dojo.event.connect(boxNode, "onmouseout", function(e) {
+		if(_this.autoHide) {
+			_this.timeHide();
+		}
+	});
+
+	// document events
+	dojo.event.connect(document.documentElement || document.body, "onclick", function(e) {
+		function isDesc(node, ancestor) {
+			while(node) {
+				if(node == ancestor){ return true; }
+				node = node.parentNode;
+			}
+			return false;
+		}
+		if(_this.autoHide && _this.showing
+			&& !isDesc(e.target, boxNode)
+			&& !isDesc(e.target, triggerNode) ) {
+			_this.hide();
+		}
+	});
+
+	return this;
+};
+
+/**** 
+	Strategies for displaying/hiding objects
+	This presents a standard interface for each of the effects
+*****/
+dojo.fx.html.toggle={}
+
+dojo.fx.html.toggle.plain = {
+	show: function(node, duration, explodeSrc, callback){
+		dojo.style.show(node);
+		if(dojo.lang.isFunction(callback)){ callback(); }
+	},
+
+	hide: function(node, duration, explodeSrc, callback){
+		dojo.style.hide(node);
+		if(dojo.lang.isFunction(callback)){ callback(); }
+	}
+}
+
+dojo.fx.html.toggle.fade = {
+	show: function(node, duration, explodeSrc, callback){
+		dojo.fx.html.fadeShow(node, duration, callback);
+	},
+
+	hide: function(node, duration, explodeSrc, callback){
+		dojo.fx.html.fadeHide(node, duration, callback);
+	}
+}
+
+dojo.fx.html.toggle.wipe = {
+	show: function(node, duration, explodeSrc, callback){
+		dojo.fx.html.wipeIn(node, duration, callback);
+	},
+
+	hide: function(node, duration, explodeSrc, callback){
+		dojo.fx.html.wipeOut(node, duration, callback);
+	}
+}
+
+dojo.fx.html.toggle.explode = {
+	show: function(node, duration, explodeSrc, callback){
+		dojo.fx.html.explode(explodeSrc||[0,0,0,0], node, duration, callback);
+	},
+
+	hide: function(node, duration, explodeSrc, callback){
+		dojo.fx.html.implode(node, explodeSrc||[0,0,0,0], duration, callback);
+	}
+}
+
+dojo.lang.mixin(dojo.fx, dojo.fx.html);


Property changes on: trunk/PhotoOp/root/static/js/src/fx/html.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/fx/svg.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/fx/svg.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/fx/svg.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,99 @@
+/*
+	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.fx.svg");
+
+dojo.require("dojo.svg");
+dojo.require("dojo.animation.*");
+dojo.require("dojo.event.*");
+
+dojo.fx.svg.fadeOut = function(node, duration, callback){
+	return dojo.fx.svg.fade(node, duration, dojo.svg.getOpacity(node), 0, callback);
+};
+dojo.fx.svg.fadeIn = function(node, duration, callback){
+	return dojo.fx.svg.fade(node, duration, dojo.svg.getOpacity(node), 1, callback);
+};
+dojo.fx.svg.fadeHide = function(node, duration, callback){
+	if(!duration) { duration = 150; } // why not have a default?
+	return dojo.fx.svg.fadeOut(node, duration, function(node) {
+		if(typeof callback == "function") { callback(node); }
+	});
+};
+dojo.fx.svg.fadeShow = function(node, duration, callback){
+	if(!duration) { duration = 150; } // why not have a default?
+	return dojo.fx.svg.fade(node, duration, 0, 1, callback);
+};
+dojo.fx.svg.fade = function(node, duration, startOpac, endOpac, callback){
+	var anim = new dojo.animation.Animation(
+		new dojo.math.curves.Line([startOpac],[endOpac]),
+		duration,
+		0
+	);
+	dojo.event.connect(anim, "onAnimate", function(e){
+		dojo.svg.setOpacity(node, e.x);
+	});
+	if (callback) {
+		dojo.event.connect(anim, "onEnd", function(e){
+			callback(node, anim);
+		});
+	};
+	anim.play(true);
+	return anim;
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+//	TODO
+/////////////////////////////////////////////////////////////////////////////////////////
+
+//	SLIDES
+dojo.fx.svg.slideTo = function(node, endCoords, duration, callback) { };
+dojo.fx.svg.slideBy = function(node, coords, duration, callback) { };
+dojo.fx.svg.slide = function(node, startCoords, endCoords, duration, callback) { 
+	var anim = new dojo.animation.Animation(
+		new dojo.math.curves.Line([startCoords],[endCoords]),
+		duration,
+		0
+	);
+	dojo.event.connect(anim, "onAnimate", function(e){
+		dojo.svg.setCoords(node, {x: e.x, y: e.y });
+	});
+	if (callback) {
+		dojo.event.connect(anim, "onEnd", function(e){
+			callback(node, anim);
+		});
+	};
+	anim.play(true);
+	return anim;
+};
+
+//	COLORS
+dojo.fx.svg.colorFadeIn = function(node, startRGB, duration, delay, callback) { };
+dojo.fx.svg.highlight = dojo.fx.svg.colorFadeIn;
+dojo.fx.svg.colorFadeFrom = dojo.fx.svg.colorFadeIn;
+
+dojo.fx.svg.colorFadeOut = function(node, endRGB, duration, delay, callback) { };
+dojo.fx.svg.unhighlight = dojo.fx.svg.colorFadeOut;
+dojo.fx.svg.colorFadeTo = dojo.fx.svg.colorFadeOut;
+
+dojo.fx.svg.colorFade = function(node, startRGB, endRGB, duration, callback, dontPlay) { };
+
+//	WIPES
+dojo.fx.svg.wipeIn = function(node, duration, callback, dontPlay) { };
+dojo.fx.svg.wipeInToHeight = function(node, duration, height, callback, dontPlay) { }
+dojo.fx.svg.wipeOut = function(node, duration, callback, dontPlay) { };
+
+//	Explode and Implode
+dojo.fx.svg.explode = function(startNode, endNode, duration, callback) { };
+dojo.fx.svg.explodeFromBox = function(startCoords, endNode, duration, callback) { };
+dojo.fx.svg.implode = function(startNode, endNode, duration, callback) { };
+dojo.fx.svg.implodeToBox = function(startNode, endCoords, duration, callback) { };
+dojo.fx.svg.Exploder = function(triggerNode, boxNode) { };
+
+//	html mixes in, we want SVG to remain separate


Property changes on: trunk/PhotoOp/root/static/js/src/fx/svg.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/graphics/Colorspace.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/graphics/Colorspace.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/graphics/Colorspace.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,944 @@
+/*
+	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.graphics.Colorspace");
+
+dojo.require("dojo.lang");
+dojo.require("dojo.math.matrix");
+
+//
+// to convert to YUV:
+//   c.whitePoint = 'D65';
+//   c.RGBWorkingSpace = 'pal_secam_rgb';
+//   var out = c.convert([r,g,b], 'RGB', 'XYZ');
+//
+// to convert to YIQ:
+//   c.whitePoint = 'D65';
+//   c.RGBWorkingSpace = 'ntsc_rgb';
+//   var out = c.convert([r,g,b], 'RGB', 'XYZ');
+//
+
+dojo.graphics.Colorspace =function(){
+
+	this.whitePoint = 'D65';
+	this.stdObserver = '10';
+	this.chromaticAdaptationAlg = 'bradford';
+	this.RGBWorkingSpace = 's_rgb';
+	this.useApproxCIELabMapping = 1; // see http://www.brucelindbloom.com/LContinuity.html
+
+	this.chainMaps = {
+		'RGB_to_xyY'  : ['XYZ'],
+		'xyY_to_RGB'  : ['XYZ'],
+		'RGB_to_Lab'  : ['XYZ'],
+		'Lab_to_RGB'  : ['XYZ'],
+		'RGB_to_LCHab': ['XYZ', 'Lab'],
+		'LCHab_to_RGB': ['Lab'],
+		'xyY_to_Lab'  : ['XYZ'],
+		'Lab_to_xyY'  : ['XYZ'],
+		'XYZ_to_LCHab': ['Lab'],
+		'LCHab_to_XYZ': ['Lab'],
+		'xyY_to_LCHab': ['XYZ', 'Lab'],
+		'LCHab_to_xyY': ['Lab', 'XYZ'],
+		'RGB_to_Luv'  : ['XYZ'],
+		'Luv_to_RGB'  : ['XYZ'],
+		'xyY_to_Luv'  : ['XYZ'],
+		'Luv_to_xyY'  : ['XYZ'],
+		'Lab_to_Luv'  : ['XYZ'],
+		'Luv_to_Lab'  : ['XYZ'],
+		'LCHab_to_Luv': ['Lab', 'XYZ'],
+		'Luv_to_LCHab': ['XYZ', 'Lab'],
+		'RGB_to_LCHuv'  : ['XYZ', 'Luv'],
+		'LCHuv_to_RGB'  : ['Luv', 'XYZ'],
+		'XYZ_to_LCHuv'  : ['Luv'],
+		'LCHuv_to_XYZ'  : ['Luv'],
+		'xyY_to_LCHuv'  : ['XYZ', 'Luv'],
+		'LCHuv_to_xyY'  : ['Luv', 'XYZ'],
+		'Lab_to_LCHuv'  : ['XYZ', 'Luv'],
+		'LCHuv_to_Lab'  : ['Luv', 'XYZ'],
+		'LCHab_to_LCHuv': ['Lab', 'XYZ', 'Luv'],
+		'LCHuv_to_LCHab': ['Luv', 'XYZ', 'Lab'],
+		'XYZ_to_CMY'    : ['RGB'],
+		'CMY_to_XYZ'    : ['RGB'],
+		'xyY_to_CMY'    : ['RGB'],
+		'CMY_to_xyY'    : ['RGB'],
+		'Lab_to_CMY'    : ['RGB'],
+		'CMY_to_Lab'    : ['RGB'],
+		'LCHab_to_CMY'  : ['RGB'],
+		'CMY_to_LCHab'  : ['RGB'],
+		'Luv_to_CMY'    : ['RGB'],
+		'CMY_to_Luv'    : ['RGB'],
+		'LCHuv_to_CMY'  : ['RGB'],
+		'CMY_to_LCHuv'  : ['RGB'],
+		'XYZ_to_HSL'    : ['RGB'],
+		'HSL_to_XYZ'    : ['RGB'],
+		'xyY_to_HSL'    : ['RGB'],
+		'HSL_to_xyY'    : ['RGB'],
+		'Lab_to_HSL'    : ['RGB'],
+		'HSL_to_Lab'    : ['RGB'],
+		'LCHab_to_HSL'  : ['RGB'],
+		'HSL_to_LCHab'  : ['RGB'],
+		'Luv_to_HSL'    : ['RGB'],
+		'HSL_to_Luv'    : ['RGB'],
+		'LCHuv_to_HSL'  : ['RGB'],
+		'HSL_to_LCHuv'  : ['RGB'],
+		'CMY_to_HSL'    : ['RGB'],
+		'HSL_to_CMY'    : ['RGB'],
+		'CMYK_to_HSL'   : ['RGB'],
+		'HSL_to_CMYK'   : ['RGB'],
+		'XYZ_to_HSV'    : ['RGB'],
+		'HSV_to_XYZ'    : ['RGB'],
+		'xyY_to_HSV'    : ['RGB'],
+		'HSV_to_xyY'    : ['RGB'],
+		'Lab_to_HSV'    : ['RGB'],
+		'HSV_to_Lab'    : ['RGB'],
+		'LCHab_to_HSV'  : ['RGB'],
+		'HSV_to_LCHab'  : ['RGB'],
+		'Luv_to_HSV'    : ['RGB'],
+		'HSV_to_Luv'    : ['RGB'],
+		'LCHuv_to_HSV'  : ['RGB'],
+		'HSV_to_LCHuv'  : ['RGB'],
+		'CMY_to_HSV'    : ['RGB'],
+		'HSV_to_CMY'    : ['RGB'],
+		'CMYK_to_HSV'   : ['RGB'],
+		'HSV_to_CMYK'   : ['RGB'],
+		'HSL_to_HSV'    : ['RGB'],
+		'HSV_to_HSL'    : ['RGB'],
+		'XYZ_to_CMYK'   : ['RGB'],
+		'CMYK_to_XYZ'   : ['RGB'],
+		'xyY_to_CMYK'   : ['RGB'],
+		'CMYK_to_xyY'   : ['RGB'],
+		'Lab_to_CMYK'   : ['RGB'],
+		'CMYK_to_Lab'   : ['RGB'],
+		'LCHab_to_CMYK' : ['RGB'],
+		'CMYK_to_LCHab' : ['RGB'],
+		'Luv_to_CMYK'   : ['RGB'],
+		'CMYK_to_Luv'   : ['RGB'],
+		'LCHuv_to_CMYK' : ['RGB'],
+		'CMYK_to_LCHuv' : ['RGB']
+	};
+
+
+	return this;
+}
+
+dojo.graphics.Colorspace.prototype.convert = function(col, model_from, model_to){
+
+	var k = model_from+'_to_'+model_to;
+
+	if (this[k]){
+		return this[k](col);
+	}else{
+		if (this.chainMaps[k]){
+
+			var cur = model_from;
+			var models = this.chainMaps[k].concat();
+			models.push(model_to);
+
+			for(var i=0; i<models.length; i++){
+
+				col = this.convert(col, cur, models[i]);
+				cur = models[i];
+			}
+
+			return col;
+
+		}else{
+
+			dojo.debug("Can't convert from "+model_from+' to '+model_to);
+		}
+	}
+}
+
+dojo.graphics.Colorspace.prototype.munge = function(keys, args){
+
+	if (dojo.lang.isArray(args[0])){
+		args = args[0];
+	}
+
+	var out = new Array();
+
+	for (var i=0; i<keys.length; i++){
+		out[keys.charAt(i)] = args[i];
+	}
+
+	return out;
+}
+
+dojo.graphics.Colorspace.prototype.getWhitePoint = function(){
+
+	var x = 0;
+	var y = 0;
+	var t = 0;
+
+	// ref: http://en.wikipedia.org/wiki/White_point
+	// TODO: i need some good/better white point values
+
+	switch(this.stdObserver){
+		case '2' :
+			switch(this.whitePoint){
+				case 'E'   : x=1/3    ; y=1/3    ; t=5400; break; //Equal energy
+				case 'D50' : x=0.34567; y=0.35850; t=5000; break;
+				case 'D55' : x=0.33242; y=0.34743; t=5500; break;
+				case 'D65' : x=0.31271; y=0.32902; t=6500; break;
+				case 'D75' : x=0.29902; y=0.31485; t=7500; break;
+				case 'A'   : x=0.44757; y=0.40745; t=2856; break; //Incandescent tungsten
+				case 'B'   : x=0.34842; y=0.35161; t=4874; break;
+				case 'C'   : x=0.31006; y=0.31616; t=6774; break;
+				case '9300': x=0.28480; y=0.29320; t=9300; break; //Blue phosphor monitors
+				case 'F2'  : x=0.37207; y=0.37512; t=4200; break; //Cool White Fluorescent
+				case 'F7'  : x=0.31285; y=0.32918; t=6500; break; //Narrow Band Daylight Fluorescent
+				case 'F11' : x=0.38054; y=0.37691; t=4000; break; //Narrow Band White Fluorescent
+				default: dojo.debug('White point '+this.whitePoint+" isn't defined for Std. Observer "+this.strObserver);
+			};
+			break;
+		case '10' :
+			switch(this.whitePoint){
+				case 'E'   : x=1/3    ; y=1/3    ; t=5400; break; //Equal energy
+				case 'D50' : x=0.34773; y=0.35952; t=5000; break;
+				case 'D55' : x=0.33411; y=0.34877; t=5500; break;
+				case 'D65' : x=0.31382; y=0.33100; t=6500; break;
+				case 'D75' : x=0.29968; y=0.31740; t=7500; break;
+				case 'A'   : x=0.45117; y=0.40594; t=2856; break; //Incandescent tungsten
+				case 'B'   : x=0.3498 ; y=0.3527 ; t=4874; break;
+				case 'C'   : x=0.31039; y=0.31905; t=6774; break;
+				case 'F2'  : x=0.37928; y=0.36723; t=4200; break; //Cool White Fluorescent
+				case 'F7'  : x=0.31565; y=0.32951; t=6500; break; //Narrow Band Daylight Fluorescent
+				case 'F11' : x=0.38543; y=0.37110; t=4000; break; //Narrow Band White Fluorescent
+				default: dojo.debug('White point '+this.whitePoint+" isn't defined for Std. Observer "+this.strObserver);
+			};
+			break;
+		default:
+			dojo.debug("Std. Observer "+this.strObserver+" isn't defined");
+	}
+
+	var z = 1 - x - y;
+
+	var wp = {'x':x, 'y':y, 'z':z, 't':t};
+
+	wp.Y = 1;
+
+	var XYZ = this.xyY_to_XYZ([wp.x, wp.y, wp.Y]);
+
+	wp.X = XYZ[0];
+	wp.Y = XYZ[1];
+	wp.Z = XYZ[2];
+
+	return wp
+}
+
+dojo.graphics.Colorspace.prototype.getPrimaries = function(){
+
+	// ref: http://www.fho-emden.de/~hoffmann/ciexyz29082000.pdf
+	// ref: http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
+
+	var m = [];
+
+	switch(this.RGBWorkingSpace){
+
+		case 'adobe_rgb_1998'	: m = [2.2, 'D65', 0.6400, 0.3300, 0.297361, 0.2100, 0.7100, 0.627355, 0.1500, 0.0600, 0.075285]; break;
+		case 'apple_rgb'	: m = [1.8, 'D65', 0.6250, 0.3400, 0.244634, 0.2800, 0.5950, 0.672034, 0.1550, 0.0700, 0.083332]; break;
+		case 'best_rgb'		: m = [2.2, 'D50', 0.7347, 0.2653, 0.228457, 0.2150, 0.7750, 0.737352, 0.1300, 0.0350, 0.034191]; break;
+		case 'beta_rgb'		: m = [2.2, 'D50', 0.6888, 0.3112, 0.303273, 0.1986, 0.7551, 0.663786, 0.1265, 0.0352, 0.032941]; break;
+		case 'bruce_rgb'	: m = [2.2, 'D65', 0.6400, 0.3300, 0.240995, 0.2800, 0.6500, 0.683554, 0.1500, 0.0600, 0.075452]; break;
+		case 'cie_rgb'		: m = [2.2, 'E'  , 0.7350, 0.2650, 0.176204, 0.2740, 0.7170, 0.812985, 0.1670, 0.0090, 0.010811]; break;
+		case 'color_match_rgb'	: m = [1.8, 'D50', 0.6300, 0.3400, 0.274884, 0.2950, 0.6050, 0.658132, 0.1500, 0.0750, 0.066985]; break;
+		case 'don_rgb_4'	: m = [2.2, 'D50', 0.6960, 0.3000, 0.278350, 0.2150, 0.7650, 0.687970, 0.1300, 0.0350, 0.033680]; break;
+		case 'eci_rgb'		: m = [1.8, 'D50', 0.6700, 0.3300, 0.320250, 0.2100, 0.7100, 0.602071, 0.1400, 0.0800, 0.077679]; break;
+		case 'ekta_space_ps5'	: m = [2.2, 'D50', 0.6950, 0.3050, 0.260629, 0.2600, 0.7000, 0.734946, 0.1100, 0.0050, 0.004425]; break;
+		case 'ntsc_rgb'		: m = [2.2, 'C'  , 0.6700, 0.3300, 0.298839, 0.2100, 0.7100, 0.586811, 0.1400, 0.0800, 0.114350]; break;
+		case 'pal_secam_rgb'	: m = [2.2, 'D65', 0.6400, 0.3300, 0.222021, 0.2900, 0.6000, 0.706645, 0.1500, 0.0600, 0.071334]; break;
+		case 'pro_photo_rgb'	: m = [1.8, 'D50', 0.7347, 0.2653, 0.288040, 0.1596, 0.8404, 0.711874, 0.0366, 0.0001, 0.000086]; break;
+		case 'smpte-c_rgb'	: m = [2.2, 'D65', 0.6300, 0.3400, 0.212395, 0.3100, 0.5950, 0.701049, 0.1550, 0.0700, 0.086556]; break;
+		case 's_rgb'		: m = [2.2, 'D65', 0.6400, 0.3300, 0.212656, 0.3000, 0.6000, 0.715158, 0.1500, 0.0600, 0.072186]; break;
+		case 'wide_gamut_rgb'	: m = [2.2, 'D50', 0.7350, 0.2650, 0.258187, 0.1150, 0.8260, 0.724938, 0.1570, 0.0180, 0.016875]; break;
+
+		default: dojo.debug("RGB working space "+this.RGBWorkingSpace+" isn't defined");
+	}
+
+	var p = {};
+
+	p.name = this.RGBWorkingSpace;
+	p.gamma = m[0];
+	p.wp = m[1];
+
+	p.xr = m[2];
+	p.yr = m[3];
+	p.Yr = m[4];
+
+	p.xg = m[5];
+	p.yg = m[6];
+	p.Yg = m[7];
+
+	p.xb = m[8];
+	p.yb = m[9];
+	p.Yb = m[10];
+
+	// if WP doesn't match current WP, convert the primaries over
+
+	if (p.wp != this.whitePoint){
+
+		var r = this.XYZ_to_xyY( this.chromaticAdaptation( this.xyY_to_XYZ([p.xr, p.yr, p.Yr]), p.wp, this.whitePoint ) );
+		var g = this.XYZ_to_xyY( this.chromaticAdaptation( this.xyY_to_XYZ([p.xg, p.yg, p.Yg]), p.wp, this.whitePoint ) );
+		var b = this.XYZ_to_xyY( this.chromaticAdaptation( this.xyY_to_XYZ([p.xb, p.yb, p.Yb]), p.wp, this.whitePoint ) );
+
+		p.xr = r[0];
+		p.yr = r[1];
+		p.Yr = r[2];
+
+		p.xg = g[0];
+		p.yg = g[1];
+		p.Yg = g[2];
+
+		p.xb = b[0];
+		p.yb = b[1];
+		p.Yb = b[2];
+
+		p.wp = this.whitePoint;
+	}
+
+	p.zr = 1 - p.xr - p.yr;
+	p.zg = 1 - p.xg - p.yg;
+	p.zb = 1 - p.xb - p.yb;
+
+	return p;
+}
+
+dojo.graphics.Colorspace.prototype.epsilon = function(){
+
+	return this.useApproxCIELabMapping ? 0.008856 : 216 / 24289;
+}
+
+dojo.graphics.Colorspace.prototype.kappa = function(){
+
+	return this.useApproxCIELabMapping ? 903.3 : 24389 / 27;
+}
+
+dojo.graphics.Colorspace.prototype.XYZ_to_xyY = function(){
+	var src = this.munge('XYZ', arguments);
+
+	var sum = src.X + src.Y + src.Z;
+
+	if (sum == 0){
+
+		var wp = this.getWhitePoint();
+		var x = wp.x;
+		var y = wp.y;
+	}else{
+		var x = src.X / sum;
+		var y = src.Y / sum;
+	}
+
+	var Y = src.Y;
+
+
+	return [x, y, Y];
+}
+
+dojo.graphics.Colorspace.prototype.xyY_to_XYZ = function(){
+	var src = this.munge('xyY', arguments);
+
+	if (src.y == 0){
+
+		var X = 0;
+		var Y = 0;
+		var Z = 0;
+	}else{
+		var X = (src.x * src.Y) / src.y;
+		var Y = src.Y;
+		var Z = ((1 - src.x - src.y) * src.Y) / src.y;
+	}
+
+	return [X, Y, Z];
+}
+
+dojo.graphics.Colorspace.prototype.RGB_to_XYZ = function(){
+	var src = this.munge('RGB', arguments);
+
+	var m = this.getRGB_XYZ_Matrix();
+	var pr = this.getPrimaries();
+
+	if (this.RGBWorkingSpace == 's_rgb'){
+
+		var r = (src.R > 0.04045) ? Math.pow(((src.R + 0.055) / 1.055), 2.4) : src.R / 12.92;
+		var g = (src.G > 0.04045) ? Math.pow(((src.G + 0.055) / 1.055), 2.4) : src.G / 12.92;
+		var b = (src.B > 0.04045) ? Math.pow(((src.B + 0.055) / 1.055), 2.4) : src.B / 12.92;
+
+	}else{
+
+		var r = Math.pow(src.R, pr.gamma);
+		var g = Math.pow(src.G, pr.gamma);
+		var b = Math.pow(src.B, pr.gamma);
+	}
+
+	var XYZ = dojo.math.matrix.multiply([[r, g, b]], m);
+
+	return [XYZ[0][0], XYZ[0][1], XYZ[0][2]];
+}
+
+dojo.graphics.Colorspace.prototype.XYZ_to_RGB = function(){
+	var src = this.munge('XYZ', arguments);
+
+	var mi = this.getXYZ_RGB_Matrix();
+	var pr = this.getPrimaries();
+
+	var rgb = dojo.math.matrix.multiply([[src.X, src.Y, src.Z]], mi);
+	var r = rgb[0][0];
+	var g = rgb[0][1];
+	var b = rgb[0][2];
+
+	if (this.RGBWorkingSpace == 's_rgb'){
+
+		var R = (r > 0.0031308) ? (1.055 * Math.pow(r, 1.0/2.4)) - 0.055 : 12.92 * r;
+		var G = (g > 0.0031308) ? (1.055 * Math.pow(g, 1.0/2.4)) - 0.055 : 12.92 * g;
+		var B = (b > 0.0031308) ? (1.055 * Math.pow(b, 1.0/2.4)) - 0.055 : 12.92 * b;
+	}else{
+		var R = Math.pow(r, 1/pr.gamma);
+		var G = Math.pow(g, 1/pr.gamma);
+		var B = Math.pow(b, 1/pr.gamma);
+	}
+
+	return [R, G, B];
+}
+
+dojo.graphics.Colorspace.prototype.XYZ_to_Lab = function(){
+	var src = this.munge('XYZ', arguments);
+
+	var wp = this.getWhitePoint();
+
+	var xr = src.X / wp.X;
+	var yr = src.Y / wp.Y;
+	var zr = src.Z / wp.Z;
+
+	var fx = (xr > this.epsilon()) ? Math.pow(xr, 1/3) : (this.kappa() * xr + 16) / 116;
+	var fy = (yr > this.epsilon()) ? Math.pow(yr, 1/3) : (this.kappa() * yr + 16) / 116;
+	var fz = (zr > this.epsilon()) ? Math.pow(zr, 1/3) : (this.kappa() * zr + 16) / 116;
+
+	var L = 116 * fy - 16;
+	var a = 500 * (fx - fy);
+	var b = 200 * (fy - fz);
+
+	return [L, a, b];
+}
+
+dojo.graphics.Colorspace.prototype.Lab_to_XYZ = function(){
+	var src = this.munge('Lab', arguments);
+
+	var wp = this.getWhitePoint();
+
+	var yr = (src.L > (this.kappa() * this.epsilon())) ? Math.pow((src.L + 16) / 116, 3) : src.L / this.kappa();
+
+	var fy = (yr > this.epsilon()) ? (src.L + 16) / 116 : (this.kappa() * yr + 16) / 116;
+
+	var fx = (src.a / 500) + fy;
+	var fz = fy - (src.b / 200);
+
+	var fxcube = Math.pow(fx, 3);
+	var fzcube = Math.pow(fz, 3);
+
+	var xr = (fxcube > this.epsilon()) ? fxcube : (116 * fx - 16) / this.kappa();
+	var zr = (fzcube > this.epsilon()) ? fzcube : (116 * fz - 16) / this.kappa();
+
+	var X = xr * wp.X;
+	var Y = yr * wp.Y;
+	var Z = zr * wp.Z;
+
+	return [X, Y, Z];
+}
+
+dojo.graphics.Colorspace.prototype.Lab_to_LCHab = function(){
+	var src = this.munge('Lab', arguments);
+
+	var L = src.L;
+	var C = Math.pow(src.a * src.a + src.b * src.b, 0.5);
+	var H = Math.atan2(src.b, src.a) * (180 / Math.PI);
+
+	if (H < 0){ H += 360; }
+	if (H > 360){ H -= 360; }
+
+	return [L, C, H];
+}
+
+dojo.graphics.Colorspace.prototype.LCHab_to_Lab = function(){
+	var src = this.munge('LCH', arguments);
+
+	var H_rad = src.H * (Math.PI / 180);
+
+	var L = src.L;
+
+	var a = src.C / Math.pow(Math.pow(Math.tan(H_rad), 2) + 1, 0.5);
+	if ((90 < src.H) && (src.H < 270)){ a= -a; }
+
+	var b = Math.pow(Math.pow(src.C, 2) - Math.pow(a, 2), 0.5);
+	if (src.H > 180){ b = -b; }
+
+	return [L, a, b];
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// this function converts an XYZ color array (col) from one whitepoint (src_w) to another (dst_w)
+//
+
+dojo.graphics.Colorspace.prototype.chromaticAdaptation = function(col, src_w, dst_w){
+
+	col = this.munge('XYZ', [col]);
+
+	//
+	// gather white point data for the source and dest
+	//
+
+	var old_wp = this.whitePoint;
+
+	this.whitePoint = src_w;
+	var wp_src = this.getWhitePoint();
+
+	this.whitePoint = dst_w;
+	var wp_dst = this.getWhitePoint();
+
+	this.whitePoint = old_wp;
+
+
+	//
+	// get a transformation matricies
+	//
+
+	switch(this.chromaticAdaptationAlg){
+		case 'xyz_scaling':
+			var ma = [[1,0,0],[0,1,0],[0,0,1]];
+			var mai = [[1,0,0],[0,1,0],[0,0,1]];
+			break;
+		case 'bradford':
+			var ma = [[0.8951, -0.7502, 0.0389],[0.2664, 1.7135, -0.0685],[-0.1614, 0.0367, 1.0296]];
+			var mai = [[0.986993, 0.432305, -0.008529],[-0.147054, 0.518360, 0.040043],[0.159963, 0.049291, 0.968487]];
+			break;
+		case 'von_kries':
+			var ma = [[0.40024, -0.22630, 0.00000],[0.70760, 1.16532, 0.00000],[-0.08081, 0.04570, 0.91822]]
+			var mai = [[1.859936, 0.361191, 0.000000],[-1.129382, 0.638812, 0.000000],[0.219897, -0.000006, 1.089064]]
+			break;
+		default:
+			dojo.debug("The "+this.chromaticAdaptationAlg+" chromatic adaptation algorithm matricies are not defined");
+	}
+
+
+	//
+	// calculate the cone response domains
+	//
+
+	var domain_src = dojo.math.matrix.multiply( [[wp_src.x, wp_src.y, wp_src.z]], ma);
+	var domain_dst = dojo.math.matrix.multiply( [[wp_dst.x, wp_dst.y, wp_dst.z]], ma);
+
+
+	//
+	// construct the centre matrix
+	//
+
+	var centre = [
+		[domain_dst[0][0]/domain_src[0][0], 0, 0],
+		[0, domain_dst[0][1]/domain_src[0][1], 0],
+		[0, 0, domain_dst[0][2]/domain_src[0][2]]
+	];
+
+
+	//
+	// caclulate 'm'
+	//
+
+	var m = dojo.math.matrix.multiply( dojo.math.matrix.multiply( ma, centre ), mai );
+
+
+	//
+	// construct source color matrix
+	//
+
+	var dst = dojo.math.matrix.multiply( [[ col.X, col.Y, col.Z ]], m );
+
+	return dst[0];
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////
+
+dojo.graphics.Colorspace.prototype.getRGB_XYZ_Matrix = function(){
+
+	var wp = this.getWhitePoint();
+	var pr = this.getPrimaries();
+
+	var Xr = pr.xr / pr.yr;
+	var Yr = 1;
+	var Zr = (1 - pr.xr - pr.yr) / pr.yr;
+
+	var Xg = pr.xg / pr.yg;
+	var Yg = 1;
+	var Zg = (1 - pr.xg - pr.yg) / pr.yg;
+
+	var Xb = pr.xb / pr.yb;
+	var Yb = 1;
+	var Zb = (1 - pr.xb - pr.yb) / pr.yb;
+
+	var m1 = [[Xr, Yr, Zr],[Xg, Yg, Zg],[Xb, Yb, Zb]];
+	var m2 = [[wp.X, wp.Y, wp.Z]];
+	var sm = dojo.math.matrix.multiply(m2, dojo.math.matrix.inverse(m1));
+
+	var Sr = sm[0][0];
+	var Sg = sm[0][1];
+	var Sb = sm[0][2];
+
+	var m4 = [[Sr*Xr, Sr*Yr, Sr*Zr],
+		  [Sg*Xg, Sg*Yg, Sg*Zg],
+		  [Sb*Xb, Sb*Yb, Sb*Zb]];
+
+	return m4;
+}
+
+dojo.graphics.Colorspace.prototype.getXYZ_RGB_Matrix = function(){
+
+	var m = this.getRGB_XYZ_Matrix();
+
+	return dojo.math.matrix.inverse(m);
+}
+
+dojo.graphics.Colorspace.prototype.XYZ_to_Luv = function(){
+
+	var src = this.munge('XYZ', arguments);
+
+	var wp = this.getWhitePoint();
+
+	var ud = (4 * src.X) / (src.X + 15 * src.Y + 3 * src.Z);
+	var vd = (9 * src.Y) / (src.X + 15 * src.Y + 3 * src.Z);
+
+	var udr = (4 * wp.X) / (wp.X + 15 * wp.Y + 3 * wp.Z);
+	var vdr = (9 * wp.Y) / (wp.X + 15 * wp.Y + 3 * wp.Z);
+
+	var yr = src.Y / wp.Y;
+
+	var L = (yr > this.epsilon()) ? 116 * Math.pow(yr, 1/3) - 16 : this.kappa() * yr;
+	var u = 13 * L * (ud-udr);
+	var v = 13 * L * (vd-vdr);
+
+	return [L, u, v];
+}
+
+dojo.graphics.Colorspace.prototype.Luv_to_XYZ = function(){
+
+	var src = this.munge('Luv', arguments);
+
+	var wp = this.getWhitePoint();
+
+	var uz = (4 * wp.X) / (wp.X + 15 * wp.Y + 3 * wp.Z);
+	var vz = (9 * wp.Y) / (wp.X + 15 * wp.Y + 3 * wp.Z);
+
+	var Y = (src.L > this.kappa() * this.epsilon()) ? Math.pow((src.L + 16) / 116, 3) : src.L / this.kappa();
+
+	var a = (1 / 3) * (((52 * src.L) / (src.u + 13 * src.L * uz)) - 1);
+	var b = -5 * Y;
+	var c = - (1 / 3);
+	var d = Y * (((39 * src.L) / (src.v + 13 * src.L * vz)) - 5);
+
+	var X = (d - b) / (a - c);
+	var Z = X * a + b;
+
+	return [X, Y, Z];
+}
+
+dojo.graphics.Colorspace.prototype.Luv_to_LCHuv = function(){
+
+	var src = this.munge('Luv', arguments);
+
+	var L = src.L;
+	var C = Math.pow(src.u * src.u + src.v * src.v, 0.5);
+	var H = Math.atan2(src.v, src.u) * (180 / Math.PI);
+
+	if (H < 0){ H += 360; }
+	if (H > 360){ H -= 360; }
+
+	return [L, C, H];
+}
+
+dojo.graphics.Colorspace.prototype.LCHuv_to_Luv = function(){
+
+	var src = this.munge('LCH', arguments);
+
+	var H_rad = src.H * (Math.PI / 180);
+
+	var L = src.L;
+	var u = src.C / Math.pow(Math.pow(Math.tan(H_rad), 2) + 1, 0.5);
+	var v = Math.pow(src.C * src.C - u * u, 0.5);
+
+	if ((90 < src.H) && (src.H < 270)){ u *= -1; }
+	if (src.H > 180){ v *= -1; }
+
+	return [L, u, v];
+}
+
+dojo.graphics.Colorspace.colorTemp_to_whitePoint = function(T){
+
+	if (T < 4000){
+		dojo.debug("Can't find a white point for temperatures under 4000K");
+		return [0,0];
+	}
+
+	if (T > 25000){
+		dojo.debug("Can't find a white point for temperatures over 25000K");
+		return [0,0];
+	}
+
+	var T1 = T;
+	var T2 = T * T;
+	var T3 = T2 * T;
+
+	var ten9 = Math.pow(10, 9);
+	var ten6 = Math.pow(10, 6);
+	var ten3 = Math.pow(10, 3);
+
+	if (T <= 7000){
+
+		var x = (-4.6070 * ten9 / T3) + (2.9678 * ten6 / T2) + (0.09911 * ten3 / T) + 0.244063;
+	}else{
+		var x = (-2.0064 * ten9 / T3) + (1.9018 * ten6 / T2) + (0.24748 * ten3 / T) + 0.237040;
+	}
+
+	var y = -3.000 * x * x + 2.870 * x - 0.275;
+
+	return [x, y];
+}
+
+dojo.graphics.Colorspace.prototype.RGB_to_CMY = function(){
+
+	var src = this.munge('RGB', arguments);
+
+	var C = 1 - src.R;
+	var M = 1 - src.G;
+	var Y = 1 - src.B;
+
+	return [C, M, Y];
+}
+
+dojo.graphics.Colorspace.prototype.CMY_to_RGB = function(){
+
+	var src = this.munge('CMY', arguments);
+
+	var R = 1 - src.C;
+	var G = 1 - src.M;
+	var B = 1 - src.Y;
+
+	return [R, G, B];
+}
+
+dojo.graphics.Colorspace.prototype.RGB_to_CMYK = function(){
+
+	var src = this.munge('RGB', arguments);
+
+	var K = Math.min(1-src.R, 1-src.G, 1-src.B);
+	var C = (1 - src.R - K) / (1 - K);
+	var M = (1 - src.G - K) / (1 - K);
+	var Y = (1 - src.B - K) / (1 - K);
+
+	return [C, M, Y, K];
+}
+
+dojo.graphics.Colorspace.prototype.CMYK_to_RGB = function(){
+
+	var src = this.munge('CMYK', arguments);
+
+	var R = 1 - Math.min(1, src.C * (1-src.K) + src.K);
+	var G = 1 - Math.min(1, src.M * (1-src.K) + src.K);
+	var B = 1 - Math.min(1, src.Y * (1-src.K) + src.K);
+
+	return [R, G, B];
+}
+
+dojo.graphics.Colorspace.prototype.CMY_to_CMYK = function(){
+
+	var src = this.munge('CMY', arguments);
+
+	var K = Math.min(src.C, src.M, src.Y);
+	var C = (src.C - K) / (1 - K);
+	var M = (src.M - K) / (1 - K);
+	var Y = (src.Y - K) / (1 - K);
+
+	return [C, M, Y, K];
+}
+
+dojo.graphics.Colorspace.prototype.CMYK_to_CMY = function(){
+
+	var src = this.munge('CMYK', arguments);
+
+	var C = Math.min(1, src.C * (1-src.K) + src.K);
+	var M = Math.min(1, src.M * (1-src.K) + src.K);
+	var Y = Math.min(1, src.Y * (1-src.K) + src.K);
+
+	return [C, M, Y];
+}
+
+dojo.graphics.Colorspace.prototype.RGB_to_HSV = function(){
+
+	var src = this.munge('RGB', arguments);
+
+	// Based on C Code in "Computer Graphics -- Principles and Practice,"
+	// Foley et al, 1996, p. 592. 
+
+	var min = Math.min(src.R, src.G, src.B);
+	var V = Math.max(src.R, src.G, src.B);
+
+	var delta = V - min;
+
+	var H = null;
+	var S = (V == 0) ? 0 : delta / V;
+
+	if (S == 0){
+		H = 0;
+	}else{
+		if (src.R == V){
+			H = 60 * (src.G - src.B) / delta;
+		}else{
+			if (src.G == V){
+				H = 120 + 60 * (src.B - src.R) / delta;
+			}else{
+				if (src.B == V){
+					// between magenta and cyan
+					H = 240 + 60 * (src.R - src.G) / delta;
+				}
+			}
+		}
+		if (H < 0){
+			H += 360;
+		}
+	}
+
+	H = (H == 0) ? 360 : H;
+
+	return [H, S, V];
+}
+
+dojo.graphics.Colorspace.prototype.HSV_to_RGB = function(){
+ 
+	var src = this.munge('HSV', arguments);
+
+	if (src.H == 360){ src.H = 0;}
+
+	// Based on C Code in "Computer Graphics -- Principles and Practice,"
+	// Foley et al, 1996, p. 593.
+
+	var r = null;
+	var g = null;
+	var b = null;
+
+	if (src.S == 0){
+		// color is on black-and-white center line
+		// achromatic: shades of gray
+		var R = src.V;
+		var G = src.V;
+		var B = src.V;
+	}else{
+		// chromatic color
+		var hTemp = src.H / 60;		// h is now IN [0,6]
+		var i = Math.floor(hTemp);	// largest integer <= h
+		var f = hTemp - i;		// fractional part of h
+
+		var p = src.V * (1 - src.S);
+		var q = src.V * (1 - (src.S * f));
+		var t = src.V * (1 - (src.S * (1 - f)));
+
+		switch(i){
+			case 0: R = src.V; G = t    ; B = p    ; break;
+			case 1: R = q    ; G = src.V; B = p    ; break;
+			case 2: R = p    ; G = src.V; B = t    ; break;
+			case 3: R = p    ; G = q    ; B = src.V; break;
+			case 4: R = t    ; G = p    ; B = src.V; break;
+			case 5: R = src.V; G = p    ; B = q    ; break;
+		}
+	}
+
+	return [R, G, B];
+}
+
+dojo.graphics.Colorspace.prototype.RGB_to_HSL = function(){
+
+	var src = this.munge('RGB', arguments);
+
+	//
+	// based on C code from http://astronomy.swin.edu.au/~pbourke/colour/hsl/
+	//
+
+
+	var min = Math.min(src.R, src.G, src.B);
+	var max = Math.max(src.R, src.G, src.B);
+	var delta = max - min;
+
+	var H = 0;
+	var S = 0;
+	var L = (min + max) / 2;
+
+	if ((L > 0) && (L < 1)){
+		S = delta / ((L < 0.5) ? (2 * L) : (2 - 2 * L));
+	}
+
+	if (delta > 0) {
+		if ((max == src.R) && (max != src.G)){
+			H += (src.G - src.B) / delta;
+		}
+		if ((max == src.G) && (max != src.B)){
+			H += (2 + (src.B - src.R) / delta);
+		}
+		if ((max == src.B) && (max != src.R)){
+			H += (4 + (src.R - src.G) / delta);
+		}
+		H *= 60;
+	}
+
+	H = (H == 0) ? 360 : H;
+
+	return [H, S, L];
+}
+
+dojo.graphics.Colorspace.prototype.HSL_to_RGB = function(){
+ 
+	var src = this.munge('HSL', arguments);
+
+	//
+	// based on C code from http://astronomy.swin.edu.au/~pbourke/colour/hsl/
+	//
+
+	while (src.H < 0){ src.H += 360; }
+	while (src.H >= 360){ src.H -= 360; }
+
+	var R = 0;
+	var G = 0;
+	var B = 0;
+
+	if (src.H < 120){
+		R = (120 - src.H) / 60;
+		G = src.H / 60;
+		B = 0;
+	}else if (src.H < 240){
+		R = 0;
+		G = (240 - src.H) / 60;
+		B = (src.H - 120) / 60;
+	}else{
+		R = (src.H - 240) / 60;
+		G = 0;
+		B = (360 - src.H) / 60;
+	}
+
+	R = 2 * src.S * Math.min(R, 1) + (1 - src.S);
+	G = 2 * src.S * Math.min(G, 1) + (1 - src.S);
+	B = 2 * src.S * Math.min(B, 1) + (1 - src.S);
+
+	if (src.L < 0.5){
+		R = src.L * R;
+		G = src.L * G;
+		B = src.L * B;
+	}else{
+		R = (1 - src.L) * R + 2 * src.L - 1;
+		G = (1 - src.L) * G + 2 * src.L - 1;
+		B = (1 - src.L) * B + 2 * src.L - 1;
+	}
+
+	return [R, G, B];
+}


Property changes on: trunk/PhotoOp/root/static/js/src/graphics/Colorspace.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/graphics/__package__.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/graphics/__package__.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/graphics/__package__.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -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.kwCompoundRequire({
+	browser:	["dojo.graphics.htmlEffects"],
+	dashboard:	["dojo.graphics.htmlEffects"]
+});
+dojo.provide("dojo.graphics.*");


Property changes on: trunk/PhotoOp/root/static/js/src/graphics/__package__.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/graphics/color/hsl.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/graphics/color/hsl.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/graphics/color/hsl.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,144 @@
+/*
+	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.graphics.color.hsl");
+dojo.require("dojo.lang.array");
+
+dojo.lang.extend(dojo.graphics.color.Color, {
+
+	toHsl: function() {
+		return dojo.graphics.color.rgb2hsl(this.toRgb());
+	}
+});
+
+dojo.graphics.color.rgb2hsl = function(r, g, b){
+
+	if (dojo.lang.isArray(r)) {
+		b = r[2] || 0;
+		g = r[1] || 0;
+		r = r[0] || 0;
+	}
+
+	r /= 255;
+	g /= 255;
+	b /= 255;
+
+	//
+	// based on C code from http://astronomy.swin.edu.au/~pbourke/colour/hsl/
+	//
+
+	var h = null;
+	var s = null;
+	var l = null;
+
+
+	var min = Math.min(r, g, b);
+	var max = Math.max(r, g, b);
+	var delta = max - min;
+
+	l = (min + max) / 2;
+
+	s = 0;
+
+	if ((l > 0) && (l < 1)){
+		s = delta / ((l < 0.5) ? (2 * l) : (2 - 2 * l));
+	}
+
+	h = 0;
+
+	if (delta > 0) {
+		if ((max == r) && (max != g)){
+			h += (g - b) / delta;
+		}
+		if ((max == g) && (max != b)){
+			h += (2 + (b - r) / delta);
+		}
+		if ((max == b) && (max != r)){
+			h += (4 + (r - g) / delta);
+		}
+		h *= 60;
+	}
+
+	h = (h == 0) ? 360 : Math.ceil((h / 360) * 255);
+	s = Math.ceil(s * 255);
+	l = Math.ceil(l * 255);
+
+	return [h, s, l];
+}
+
+dojo.graphics.color.hsl2rgb = function(h, s, l){
+ 
+	if (dojo.lang.isArray(h)) {
+		l = h[2] || 0;
+		s = h[1] || 0;
+		h = h[0] || 0;
+	}
+
+	h = (h / 255) * 360;
+	if (h == 360){ h = 0;}
+	s = s / 255;
+	l = l / 255;
+
+	//
+	// based on C code from http://astronomy.swin.edu.au/~pbourke/colour/hsl/
+	//
+
+
+	while (h < 0){ h += 360; }
+	while (h > 360){ h -= 360; }
+	var r, g, b;
+	if (h < 120){
+		r = (120 - h) / 60;
+		g = h / 60;
+		b = 0;
+	}else if (h < 240){
+		r = 0;
+		g = (240 - h) / 60;
+		b = (h - 120) / 60;
+	}else{
+		r = (h - 240) / 60;
+		g = 0;
+		b = (360 - h) / 60;
+	}
+
+	r = Math.min(r, 1);
+	g = Math.min(g, 1);
+	b = Math.min(b, 1);
+
+	r = 2 * s * r + (1 - s);
+	g = 2 * s * g + (1 - s);
+	b = 2 * s * b + (1 - s);
+
+	if (l < 0.5){
+		r = l * r;
+		g = l * g;
+		b = l * b;
+	}else{
+		r = (1 - l) * r + 2 * l - 1;
+		g = (1 - l) * g + 2 * l - 1;
+		b = (1 - l) * b + 2 * l - 1;
+	}
+
+	r = Math.ceil(r * 255);
+	g = Math.ceil(g * 255);
+	b = Math.ceil(b * 255);
+
+	return [r, g, b];
+}
+
+dojo.graphics.color.hsl2hex = function(h, s, l){
+	var rgb = dojo.graphics.color.hsl2rgb(h, s, l);
+	return dojo.graphics.color.rgb2hex(rgb[0], rgb[1], rgb[2]);
+}
+
+dojo.graphics.color.hex2hsl = function(hex){
+	var rgb = dojo.graphics.color.hex2rgb(hex);
+	return dojo.graphics.color.rgb2hsl(rgb[0], rgb[1], rgb[2]);
+}


Property changes on: trunk/PhotoOp/root/static/js/src/graphics/color/hsl.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/graphics/color/hsv.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/graphics/color/hsv.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/graphics/color/hsv.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,141 @@
+/*
+	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.graphics.color.hsv");
+dojo.require("dojo.lang.array");
+
+dojo.lang.extend(dojo.graphics.color.Color, {
+
+	toHsv: function() {
+		return dojo.graphics.color.rgb2hsv(this.toRgb());
+	}
+
+});
+
+dojo.graphics.color.rgb2hsv = function(r, g, b){
+
+	if (dojo.lang.isArray(r)) {
+		b = r[2] || 0;
+		g = r[1] || 0;
+		r = r[0] || 0;
+	}
+
+	// r,g,b, each 0 to 255, to HSV.
+	// h = 0.0 to 360.0 (corresponding to 0..360.0 degrees around hexcone)
+	// s = 0.0 (shade of gray) to 1.0 (pure color)
+	// v = 0.0 (black) to 1.0 {white)
+	//
+	// Based on C Code in "Computer Graphics -- Principles and Practice,"
+	// Foley et al, 1996, p. 592. 
+	//
+	// our calculatuions are based on 'regular' values (0-360, 0-1, 0-1) 
+	// but we return bytes values (0-255, 0-255, 0-255)
+
+	var h = null;
+	var s = null;
+	var v = null;
+
+	var min = Math.min(r, g, b);
+	v = Math.max(r, g, b);
+
+	var delta = v - min;
+
+	// calculate saturation (0 if r, g and b are all 0)
+
+	s = (v == 0) ? 0 : delta/v;
+
+	if (s == 0){
+		// achromatic: when saturation is, hue is undefined
+		h = 0;
+	}else{
+		// chromatic
+		if (r == v){
+			// between yellow and magenta
+			h = 60 * (g - b) / delta;
+		}else{
+			if (g == v){
+				// between cyan and yellow
+				h = 120 + 60 * (b - r) / delta;
+			}else{
+				if (b == v){
+					// between magenta and cyan
+					h = 240 + 60 * (r - g) / delta;
+				}
+			}
+		}
+		if (h < 0){
+			h += 360;
+		}
+	}
+
+
+	h = (h == 0) ? 360 : Math.ceil((h / 360) * 255);
+	s = Math.ceil(s * 255);
+
+	return [h, s, v];
+}
+
+dojo.graphics.color.hsv2rgb = function(h, s, v){
+ 
+	if (dojo.lang.isArray(h)) {
+		v = h[2] || 0;
+		s = h[1] || 0;
+		h = h[0] || 0;
+	}
+
+	h = (h / 255) * 360;
+	if (h == 360){ h = 0;}
+
+	s = s / 255;
+	v = v / 255;
+
+	// Based on C Code in "Computer Graphics -- Principles and Practice,"
+	// Foley et al, 1996, p. 593.
+	//
+	// H = 0.0 to 360.0 (corresponding to 0..360 degrees around hexcone) 0 for S = 0
+	// S = 0.0 (shade of gray) to 1.0 (pure color)
+	// V = 0.0 (black) to 1.0 (white)
+
+	var r = null;
+	var g = null;
+	var b = null;
+
+	if (s == 0){
+		// color is on black-and-white center line
+		// achromatic: shades of gray
+		r = v;
+		g = v;
+		b = v;
+	}else{
+		// chromatic color
+		var hTemp = h / 60;		// h is now IN [0,6]
+		var i = Math.floor(hTemp);	// largest integer <= h
+		var f = hTemp - i;		// fractional part of h
+
+		var p = v * (1 - s);
+		var q = v * (1 - (s * f));
+		var t = v * (1 - (s * (1 - f)));
+
+		switch(i){
+			case 0: r = v; g = t; b = p; break;
+			case 1: r = q; g = v; b = p; break;
+			case 2: r = p; g = v; b = t; break;
+			case 3: r = p; g = q; b = v; break;
+			case 4: r = t; g = p; b = v; break;
+			case 5: r = v; g = p; b = q; break;
+		}
+	}
+
+	r = Math.ceil(r * 255);
+	g = Math.ceil(g * 255);
+	b = Math.ceil(b * 255);
+
+	return [r, g, b];
+}


Property changes on: trunk/PhotoOp/root/static/js/src/graphics/color/hsv.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/graphics/color.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/graphics/color.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/graphics/color.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,166 @@
+/*
+	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.graphics.color");
+dojo.require("dojo.lang.array");
+
+// TODO: rewrite the "x2y" methods to take advantage of the parsing
+//       abilities of the Color object. Also, beef up the Color
+//       object (as possible) to parse most common formats
+
+// takes an r, g, b, a(lpha) value, [r, g, b, a] array, "rgb(...)" string, hex string (#aaa, #aaaaaa, aaaaaaa)
+dojo.graphics.color.Color = function(r, g, b, a) {
+	// dojo.debug("r:", r[0], "g:", r[1], "b:", r[2]);
+	if(dojo.lang.isArray(r)) {
+		this.r = r[0];
+		this.g = r[1];
+		this.b = r[2];
+		this.a = r[3]||1.0;
+	} 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.0;
+	} 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(includeAlpha) {
+		if(includeAlpha) {
+			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(); // decent default?
+	},
+
+	blend: function(color, weight) {
+		return dojo.graphics.color.blend(this.toRgb(), new dojo.graphics.color.Color(color).toRgb(), weight);
+	}
+});
+
+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]
+};
+
+// blend colors a and b (both as RGB array or hex strings) with weight from -1 to +1, 0 being a 50/50 blend
+dojo.graphics.color.blend = function(a, b, weight) {
+	if(typeof a == "string") { return dojo.graphics.color.blendHex(a, b, weight); }
+	if(!weight) { weight = 0; }
+	else if(weight > 1) { weight = 1; }
+	else if(weight < -1) { weight = -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 * weight));
+	}
+	return c;
+}
+
+// very convenient blend that takes and returns hex values
+// (will get called automatically by blend when blend gets strings)
+dojo.graphics.color.blendHex = function(a, b, weight) {
+	return dojo.graphics.color.rgb2hex(dojo.graphics.color.blend(dojo.graphics.color.hex2rgb(a), dojo.graphics.color.hex2rgb(b), weight));
+}
+
+// get RGB array from css-style color declarations
+dojo.graphics.color.extractRGB = function(color) {
+	var hex = "0123456789abcdef";
+	color = color.toLowerCase();
+	if( color.indexOf("rgb") == 0 ) {
+		var matches = color.match(/rgba*\((\d+), *(\d+), *(\d+)/i);
+		var ret = matches.splice(1, 3);
+		return ret;
+	} else {
+		var colors = dojo.graphics.color.hex2rgb(color);
+		if(colors) {
+			return colors;
+		} else {
+			// named color (how many do we support?)
+			return dojo.graphics.color.named[color] || [255, 255, 255];
+		}
+	}
+}
+
+dojo.graphics.color.hex2rgb = function(hex) {
+	var hexNum = "0123456789ABCDEF";
+	var rgb = new Array(3);
+	if( hex.indexOf("#") == 0 ) { hex = hex.substring(1); }
+	hex = hex.toUpperCase();
+	if(hex.replace(new RegExp("["+hexNum+"]", "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; i < rgb.length; i++) {
+		rgb[i] = hexNum.indexOf(rgb[i].charAt(0)) * 16 + hexNum.indexOf(rgb[i].charAt(1));
+	}
+	return rgb;
+}
+
+dojo.graphics.color.rgb2hex = function(r, g, b) {
+	if(dojo.lang.isArray(r)) {
+		g = r[1] || 0;
+		b = r[2] || 0;
+		r = r[0] || 0;
+	}
+	var ret = dojo.lang.map([r, g, b], function(x) {
+		x = new Number(x);
+		var s = x.toString(16);
+		while(s.length < 2) { s = "0" + s; }
+		return s;
+	});
+	ret.unshift("#");
+	return ret.join("");
+}


Property changes on: trunk/PhotoOp/root/static/js/src/graphics/color.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/hostenv_adobesvg.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/hostenv_adobesvg.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/hostenv_adobesvg.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,571 @@
+/*
+	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
+*/
+
+/*
+ * Adobe SVG Viewer host environment
+ */
+if(typeof window == 'undefined'){
+	dojo.raise("attempt to use adobe svg hostenv when no window object");
+}
+
+with(dojo.render){
+	name = navigator.appName;
+	ver = parseFloat(navigator.appVersion, 10);
+	switch(navigator.platform){
+		case "MacOS":
+			os.osx =  true;
+			break;
+		case "Linux":
+			os.linux =  true;
+			break;
+		case "Windows":
+			os.win =  true;
+			break;
+		default:
+			os.linux = true;
+			break;
+	};
+	svg.capable = true;
+	svg.support.builtin = true;
+	svg.adobe = true;
+};
+
+// browserEval("alert(window.location);");
+
+dojo.hostenv.println = function(s){
+	try{
+    // FIXME: this may not work with adobe's viewer, as we may first need a 
+		// reference to the svgDocument
+		// FIXME: need a way to determine where to position the text for this
+    var ti = document.createElement("text");
+    ti.setAttribute("x","50");
+		var yPos = 25 + 15*document.getElementsByTagName("text").length;
+    ti.setAttribute("y",yPos);
+		var tn = document.createTextNode(s);
+		ti.appendChild(tn);
+		document.documentElement.appendChild(ti);
+	}catch(e){
+
+	}
+}
+
+dojo.debug = function() {
+	if (!djConfig.isDebug) { return; }
+	var args = arguments;
+	if(typeof dojo.hostenv.println != 'function'){
+		dojo.raise("attempt to call dojo.debug when there is no dojo.hostenv println implementation (yet?)");
+	}
+	var isJUM = dj_global["jum"];
+	var s = isJUM ? "": "DEBUG: ";
+	for(var i=0;i<args.length;++i){ s += args[i]; }
+	if(isJUM){ // this seems to be the only way to get JUM to "play nice"
+		jum.debug(s);
+	}else{
+		dojo.hostenv.println(s);
+	}
+}
+
+dojo.hostenv.startPackage("dojo.hostenv");
+
+dojo.hostenv.name_ = 'adobesvg';
+
+dojo.hostenv.anonCtr = 0;
+dojo.hostenv.anon = {};
+
+dojo.hostenv.nameAnonFunc = function(anonFuncPtr, namespaceObj){
+	var ret = "_"+this.anonCtr++;
+	var nso = (namespaceObj || this.anon);
+	while(typeof nso[ret] != "undefined"){
+		ret = "_"+this.anonCtr++;
+	}
+	nso[ret] = anonFuncPtr;
+	return ret;
+}
+
+dojo.hostenv.modulesLoadedFired = false;
+dojo.hostenv.modulesLoadedListeners = [];
+dojo.hostenv.getTextStack = [];
+dojo.hostenv.loadUriStack = [];
+dojo.hostenv.loadedUris = [];
+
+
+dojo.hostenv.modulesLoaded = function(){
+	if(this.modulesLoadedFired){ return; }
+	if((this.loadUriStack.length==0)&&(this.getTextStack.length==0)){
+		if(this.inFlightCount > 0){ 
+			dojo.debug("couldn't initialize, there are files still in flight");
+			return;
+		}
+		this.modulesLoadedFired = true;
+		var mll = this.modulesLoadedListeners;
+		for(var x=0; x<mll.length; x++){
+			mll[x]();
+		}
+	}
+}
+
+dojo.hostenv.getNewAnonFunc = function(){
+	var ret = "_"+this.anonCtr++;
+	while(typeof this.anon[ret] != "undefined"){
+		ret = "_"+this.anonCtr++;
+	}
+	// this.anon[ret] = function(){};
+	eval("dojo.nostenv.anon."+ret+" = function(){};");
+	return [ret, this.anon[ret]];
+}
+
+dojo.hostenv.displayStack = function(){
+	var oa = [];
+	var stack = this.loadUriStack;
+	for(var x=0; x<stack.length; x++){
+		oa.unshift([stack[x][0], (typeof stack[x][2])]);
+	}
+	dojo.debug("<pre>"+oa.join("\n")+"</pre>");
+}
+
+dojo.hostenv.unwindUriStack = function(){
+	var stack = this.loadUriStack;
+	for(var x in dojo.hostenv.loadedUris){
+		for(var y=stack.length-1; y>=0; y--){
+			if(stack[y][0]==x){
+				stack.splice(y, 1);
+			}
+		}
+	}
+	var next = stack.pop();
+	if((!next)&&(stack.length==0)){ 
+		return;
+	}
+	for(var x=0; x<stack.length; x++){
+		if((stack[x][0]==next[0])&&(stack[x][2])){
+			next[2] == stack[x][2]
+		}
+	}
+	var last = next;
+	while(dojo.hostenv.loadedUris[next[0]]){
+		last = next;
+		next = stack.pop();
+	}
+	while(typeof next[2] == "string"){ // unwind as far as we can
+		try{
+			// dojo.debug("<pre><![CDATA["+next[2]+"]]></pre>");
+			dj_eval(next[2]);
+			next[1](true);
+		}catch(e){
+			dojo.debug("we got an error when loading "+next[0]);
+			dojo.debug("error: "+e);
+			// for(var x in e){ alert(x+" "+e[x]); }
+		}
+		dojo.hostenv.loadedUris[next[0]] = true;
+		dojo.hostenv.loadedUris.push(next[0]);
+		last = next;
+		next = stack.pop();
+		if((!next)&&(stack.length==0)){ break; }
+		while(dojo.hostenv.loadedUris[next[0]]){
+			last = next;
+			next = stack.pop();
+		}
+	}
+	if(next){
+		stack.push(next);
+		dojo.debug("### CHOKED ON: "+next[0]);
+	}
+}
+
+/**
+ * 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.
+ */
+dojo.hostenv.loadUri = function(uri, cb){
+	if(dojo.hostenv.loadedUris[uri]){
+		return;
+	}
+	var stack = this.loadUriStack;
+	stack.push([uri, cb, null]);
+	var tcb = function(contents){
+		// gratuitous hack for Adobe SVG 3, what a fucking POS
+		if(contents.content){
+			contents = contents.content;
+		}
+
+		// stack management
+		var next = stack.pop();
+		if((!next)&&(stack.length==0)){ 
+			dojo.hostenv.modulesLoaded();
+			return;
+		}
+		if(typeof contents == "string"){
+			stack.push(next);
+			for(var x=0; x<stack.length; x++){
+				if(stack[x][0]==uri){
+					stack[x][2] = contents;
+				}
+			}
+			next = stack.pop();
+		}
+		if(dojo.hostenv.loadedUris[next[0]]){ 
+			// dojo.debug("WE ALREADY HAD: "+next[0]);
+			dojo.hostenv.unwindUriStack();
+			return;
+		}
+		// push back onto stack
+		stack.push(next);
+		if(next[0]!=uri){
+			//  and then unwind as far as we can
+			if(typeof next[2] == "string"){
+				dojo.hostenv.unwindUriStack();
+			}
+
+		}else{
+			if(!contents){ 
+				next[1](false);
+			}else{
+				var deps = dojo.hostenv.getDepsForEval(next[2]);
+				if(deps.length>0){
+					eval(deps.join(";"));
+				}else{
+					dojo.hostenv.unwindUriStack();
+				}
+			}
+		}
+	}
+	this.getText(uri, tcb, true);
+}
+
+/**
+ * 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.
+ */
+dojo.hostenv.loadUri = function(uri, cb){
+	if(dojo.hostenv.loadedUris[uri]){
+		return;
+	}
+	var stack = this.loadUriStack;
+	stack.push([uri, cb, null]);
+	var tcb = function(contents){
+		// gratuitous hack for Adobe SVG 3, what a fucking POS
+		if(contents.content){
+			contents = contents.content;
+		}
+
+		// stack management
+		var next = stack.pop();
+		if((!next)&&(stack.length==0)){ 
+			dojo.hostenv.modulesLoaded();
+			return;
+		}
+		if(typeof contents == "string"){
+			stack.push(next);
+			for(var x=0; x<stack.length; x++){
+				if(stack[x][0]==uri){
+					stack[x][2] = contents;
+				}
+			}
+			next = stack.pop();
+		}
+		if(dojo.hostenv.loadedUris[next[0]]){ 
+			// dojo.debug("WE ALREADY HAD: "+next[0]);
+			dojo.hostenv.unwindUriStack();
+			return;
+		}
+		// push back onto stack
+		stack.push(next);
+		if(next[0]!=uri){
+			//  and then unwind as far as we can
+			if(typeof next[2] == "string"){
+				dojo.hostenv.unwindUriStack();
+			}
+
+		}else{
+			if(!contents){ 
+				next[1](false);
+			}else{
+				var deps = dojo.hostenv.getDepsForEval(next[2]);
+				if(deps.length>0){
+					eval(deps.join(";"));
+				}else{
+					dojo.hostenv.unwindUriStack();
+				}
+			}
+		}
+	}
+	this.getText(uri, tcb, true);
+}
+
+/**
+* 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.loadModule = function(modulename, exact_only, omit_module_check){
+	// alert("dojo.hostenv.loadModule('"+modulename+"');");
+	var module = this.findModule(modulename, 0);
+	if(module){
+		return module;
+	}
+
+	// dojo.debug("dojo.hostenv.loadModule('"+modulename+"');");
+
+	// protect against infinite recursion from mutual dependencies
+	if (typeof this.loading_modules_[modulename] !== 'undefined'){
+		// NOTE: this should never throw an exception!! "recursive" includes
+		// are normal in the course of app and module building, so blow out of
+		// it gracefully, but log it in debug mode
+
+		// dojo.raise("recursive attempt to load module '" + modulename + "'");
+		dojo.debug("recursive attempt to load module '" + modulename + "'");
+	}else{
+		this.addedToLoadingCount.push(modulename);
+	}
+	this.loading_modules_[modulename] = 1;
+
+
+	// convert periods to slashes
+	var relpath = modulename.replace(/\./g, '/') + '.js';
+
+	var syms = modulename.split(".");
+	var nsyms = modulename.split(".");
+	if(syms[0]=="dojo"){ // FIXME: need a smarter way to do this!
+		syms[0] = "src"; 
+	}
+	var last = syms.pop();
+	syms.push(last);
+	// figure out if we're looking for a full package, if so, we want to do
+	// things slightly diffrently
+	var _this = this;
+	var pfn = this.pkgFileName;
+	if(last=="*"){
+		modulename = (nsyms.slice(0, -1)).join('.');
+
+		var module = this.findModule(modulename, 0);
+		// dojo.debug("found: "+modulename+"="+module);
+		if(module){
+			_this.removedFromLoadingCount.push(modulename);
+			return module;
+		}
+
+		var nextTry = function(lastStatus){
+			if(lastStatus){ 
+				module = _this.findModule(modulename, false); // pass in false so we can give better error
+				if((!module)&&(syms[syms.length-1]!=pfn)){
+					dojo.raise("Module symbol '" + modulename + "' is not defined after loading '" + relpath + "'"); 
+				}
+				if(module){
+					_this.removedFromLoadingCount.push(modulename);
+					dojo.hostenv.modulesLoaded();
+					return;
+				}
+			}
+			syms.pop();
+			syms.push(pfn);
+			// dojo.debug("syms: "+syms);
+			relpath = syms.join("/") + '.js';
+			if(relpath.charAt(0)=="/"){
+				relpath = relpath.slice(1);
+			}
+			// dojo.debug("relpath: "+relpath);
+			_this.loadPath(relpath, ((!omit_module_check) ? modulename : null), nextTry);
+		}
+
+		nextTry();
+	}else{
+		relpath = syms.join("/") + '.js';
+		modulename = nsyms.join('.');
+
+		var nextTry = function(lastStatus){
+			// dojo.debug("lastStatus: "+lastStatus);
+			if(lastStatus){ 
+				// dojo.debug("inital relpath: "+relpath);
+				module = _this.findModule(modulename, false); // pass in false so we can give better error
+				// if(!module){
+				if((!module)&&(syms[syms.length-1]!=pfn)){
+					dojo.raise("Module symbol '" + modulename + "' is not defined after loading '" + relpath + "'"); 
+				}
+				if(module){
+					_this.removedFromLoadingCount.push(modulename);
+					dojo.hostenv.modulesLoaded();
+					return;
+				}
+			}
+			var setPKG = (syms[syms.length-1]==pfn) ? false : true;
+			syms.pop();
+			if(setPKG){
+				syms.push(pfn);
+			}
+			relpath = syms.join("/") + '.js';
+			if(relpath.charAt(0)=="/"){
+				relpath = relpath.slice(1);
+			}
+			// dojo.debug("relpath: "+relpath);
+			_this.loadPath(relpath, ((!omit_module_check) ? modulename : null), nextTry);
+		}
+
+		this.loadPath(relpath, ((!omit_module_check) ? modulename : null), nextTry);
+	}
+	return;
+}
+
+/**
+ * Read the contents of the specified uri and return those contents.
+ *
+ * FIXME: Make sure this is consistent with other implementations of getText
+ * @param uri A relative or absolute uri. If absolute, it still must be in the same "domain" as we are.
+ * @param async_cb If not specified, returns false as synchronous is not
+ * supported. If specified, load asynchronously, and use async_cb as the handler which receives the result of the request.
+ * @param fail_ok Default false. If fail_ok and !async_cb and loading fails, return null instead of throwing.
+ */ 
+dojo.hostenv.async_cb = null;
+
+dojo.hostenv.unWindGetTextStack = function(){
+	if(dojo.hostenv.inFlightCount>0){
+		setTimeout("dojo.hostenv.unWindGetTextStack()", 100);
+		return;
+	}
+	// we serialize because this goddamned environment is too fucked up
+	// to know how to do anything else
+	dojo.hostenv.inFlightCount++;
+	var next = dojo.hostenv.getTextStack.pop();
+	if((!next)&&(dojo.hostenv.getTextStack.length==0)){ 
+		dojo.hostenv.inFlightCount--;
+		dojo.hostenv.async_cb = function(){};
+		return;
+	}
+	dojo.hostenv.async_cb = next[1];
+	// http = window.getURL(uri, dojo.hostenv.anon[cbn]);
+	window.getURL(next[0], function(result){ 
+		dojo.hostenv.inFlightCount--;
+		dojo.hostenv.async_cb(result.content);
+		dojo.hostenv.unWindGetTextStack();
+	});
+}
+
+dojo.hostenv.getText = function(uri, async_cb, fail_ok){
+	// dojo.debug("Calling getText()");
+	try{
+		if(async_cb){
+			dojo.hostenv.getTextStack.push([uri, async_cb, fail_ok]);
+			dojo.hostenv.unWindGetTextStack();
+		}else{
+			return dojo.raise("No synchronous XMLHTTP implementation available, for uri " + uri);
+		}
+	}catch(e){
+		return dojo.raise("No XMLHTTP implementation available, for uri " + uri);
+	}
+}
+
+
+/**
+ * Makes an async post to the specified uri.
+ *
+ * FIXME: Not sure that we need this, but adding for completeness.
+ * More details about the implementation of this are available at 
+ * http://wiki.svg.org/index.php/PostUrl
+ * @param uri A relative or absolute uri. If absolute, it still must be in the same "domain" as we are.
+ * @param async_cb If not specified, returns false as synchronous is not
+ * supported. If specified, load asynchronously, and use async_cb as the progress handler which takes the xmlhttp object as its argument. If async_cb, this function returns null.
+ * @param text Data to post
+ * @param fail_ok Default false. If fail_ok and !async_cb and loading fails, return null instead of throwing.
+ * @param mime_type optional MIME type of the posted data (such as "text/plain")
+ * @param encoding optional encoding for data. null, 'gzip' and 'deflate' are possible values. If browser does not support binary post this parameter is ignored.
+ */ 
+dojo.hostenv.postText = function(uri, async_cb, text, fail_ok, mime_type, encoding){
+	var http = null;
+	
+	var async_callback = function(httpResponse){
+		if (!httpResponse.success) {
+			dojo.raise("Request for uri '" + uri + "' resulted in " + httpResponse.status);
+		}
+		
+		if(!httpResponse.content) {
+			if (!fail_ok) dojo.raise("Request for uri '" + uri + "' resulted in no content");
+			return null;
+		}
+		// FIXME: wtf, I'm losing a reference to async_cb
+		async_cb(httpResponse.content);
+	}
+	
+	try {
+		if(async_cb) {
+			http = window.postURL(uri, text, async_callback, mimeType, encoding);
+		} else {
+		return dojo.raise("No synchronous XMLHTTP post implementation available, for uri " + uri);
+		}
+	} catch(e) {
+		return dojo.raise("No XMLHTTP post implementation available, for uri " + uri);
+	}
+}
+
+/*
+ * It turns out that if we check *right now*, as this script file is being loaded,
+ * then the last script element in the window DOM is ourselves.
+ * That is because any subsequent script elements haven't shown up in the document
+ * object yet.
+ */
+function dj_last_script_src() {
+	var scripts = window.document.getElementsByTagName('script');
+	if(scripts.length < 1){ 
+		dojo.raise("No script elements in window.document, so can't figure out my script src"); 
+	}
+	var li = scripts.length-1;
+	var xlinkNS = "http://www.w3.org/1999/xlink";
+	var src = null;
+	var script = null;
+	while(!src){
+		script = scripts.item(li);
+		src = script.getAttributeNS(xlinkNS,"href");
+		li--;
+		if(li<0){ break; }
+		// break;
+	}
+	if(!src){
+		dojo.raise("Last script element (out of " + scripts.length + ") has no src");
+	}
+	return src;
+}
+
+if(!dojo.hostenv["library_script_uri_"]){
+	dojo.hostenv.library_script_uri_ = dj_last_script_src();
+}
+
+// dojo.hostenv.loadUri = function(uri){
+	/* FIXME: adding a script element doesn't seem to be synchronous, and so
+	 * checking for namespace or object existance after loadUri using this
+	 * method will error out. Need to figure out some other way of handling
+	 * this!
+	 */
+	/*
+	var se = document.createElement("script");
+	se.src = uri;
+	var head = document.getElementsByTagName("head")[0];
+	head.appendChild(se);
+	// document.write("<script type='text/javascript' src='"+uri+"' />");
+	return 1;
+}
+*/


Property changes on: trunk/PhotoOp/root/static/js/src/hostenv_adobesvg.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/hostenv_browser.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/hostenv_browser.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/hostenv_browser.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,360 @@
+/*
+	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
+*/
+
+if(typeof window == 'undefined'){
+	dojo.raise("no window object");
+}
+
+// attempt to figure out the path to dojo if it isn't set in the config
+(function() {
+	// before we get any further with the config options, try to pick them out
+	// of the URL. Most of this code is from NW
+	if(djConfig.allowQueryConfig){
+		var baseUrl = document.location.toString(); // FIXME: use location.query instead?
+		var params = baseUrl.split("?", 2);
+		if(params.length > 1){
+			var paramStr = params[1];
+			var pairs = paramStr.split("&");
+			for(var x in pairs){
+				var sp = pairs[x].split("=");
+				// FIXME: is this eval dangerous?
+				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 scripts = document.getElementsByTagName("script");
+		var rePkg = /(__package__|dojo|bootstrap1)\.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) {
+				var root = src.substring(0, m.index);
+				if(src.indexOf("bootstrap1") > -1) { root += "../"; }
+				if(!this["djConfig"]) { djConfig = {}; }
+				if(djConfig["baseScriptUri"] == "") { djConfig["baseScriptUri"] = root; }
+				if(djConfig["baseRelativePath"] == "") { djConfig["baseRelativePath"] = root; }
+				break;
+			}
+		}
+	}
+
+	// fill in the rendering support information in dojo.render.*
+	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;
+	// could also be Solaris or something, but it's the same browser
+	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 geckoPos = dua.indexOf("Gecko");
+	drh.mozilla = drh.moz = (geckoPos >= 0)&&(!drh.khtml);
+	if (drh.mozilla) {
+		// gecko version is YYYYMMDD
+		drh.geckoVersion = dua.substring(geckoPos + 6, geckoPos + 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;
+
+	// TODO: is the HTML LANG attribute relevant?
+	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 = [];
+
+// These are in order of decreasing likelihood; this will change in time.
+dojo.hostenv._XMLHTTP_PROGIDS = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'];
+
+dojo.hostenv.getXmlhttpObject = function(){
+    var http = null;
+	var last_e = null;
+	try{ http = new XMLHttpRequest(); }catch(e){}
+    if(!http){
+		for(var i=0; i<3; ++i){
+			var progid = dojo.hostenv._XMLHTTP_PROGIDS[i];
+			try{
+				http = new ActiveXObject(progid);
+			}catch(e){
+				last_e = e;
+			}
+
+			if(http){
+				dojo.hostenv._XMLHTTP_PROGIDS = [progid];  // so faster next time
+				break;
+			}
+		}
+
+		/*if(http && !http.toString) {
+			http.toString = function() { "[object XMLHttpRequest]"; }
+		}*/
+	}
+
+	if(!http){
+		return dojo.raise("XMLHTTP not available", last_e);
+	}
+
+	return http;
+}
+
+/**
+ * Read the contents of the specified uri and return those contents.
+ *
+ * @param uri A relative or absolute uri. If absolute, it still must be in the
+ * same "domain" as we are.
+ *
+ * @param async_cb If not specified, load synchronously. If specified, load
+ * asynchronously, and use async_cb as the progress handler which takes the
+ * xmlhttp object as its argument. If async_cb, this function returns null.
+ *
+ * @param fail_ok Default false. If fail_ok and !async_cb and loading fails,
+ * return null instead of throwing.
+ */
+dojo.hostenv.getText = function(uri, async_cb, fail_ok){
+
+	var http = this.getXmlhttpObject();
+
+	if(async_cb){
+		http.onreadystatechange = function(){
+			if(4==http.readyState){
+				if((!http["status"])||((200 <= http.status)&&(300 > http.status))){
+					// dojo.debug("LOADED URI: "+uri);
+					async_cb(http.responseText);
+				}
+			}
+		}
+	}
+
+	http.open('GET', uri, async_cb ? true : false);
+	try{
+		http.send(null);
+		if(async_cb){
+			return null;
+		}
+		if((http["status"])&&((200 > http.status)||(300 <= http.status))){
+			throw Error("Unable to load "+uri+" status:"+ http.status);
+		}
+	}catch(e){
+		if((fail_ok)&&(!async_cb)){
+			return null;
+		}else{
+			throw e;
+		}
+	}
+
+	return http.responseText;
+}
+
+/*
+ * It turns out that if we check *right now*, as this script file is being loaded,
+ * then the last script element in the window DOM is ourselves.
+ * That is because any subsequent script elements haven't shown up in the document
+ * object yet.
+ */
+ /*
+function dj_last_script_src() {
+    var scripts = window.document.getElementsByTagName('script');
+    if(scripts.length < 1){
+		dojo.raise("No script elements in window.document, so can't figure out my script src");
+	}
+    var script = scripts[scripts.length - 1];
+    var src = script.src;
+    if(!src){
+		dojo.raise("Last script element (out of " + scripts.length + ") has no src");
+	}
+    return src;
+}
+
+if(!dojo.hostenv["library_script_uri_"]){
+	dojo.hostenv.library_script_uri_ = dj_last_script_src();
+}
+*/
+
+dojo.hostenv.defaultDebugContainerId = 'dojoDebug';
+dojo.hostenv._println_buffer = [];
+dojo.hostenv._println_safe = false;
+dojo.hostenv.println = function (line){
+	if(!dojo.hostenv._println_safe){
+		dojo.hostenv._println_buffer.push(line);
+	}else{
+		try {
+			var console = document.getElementById(djConfig.debugContainerId ?
+				djConfig.debugContainerId : dojo.hostenv.defaultDebugContainerId);
+			if(!console) { console = document.getElementsByTagName("body")[0] || document.body; }
+
+			var div = document.createElement("div");
+			div.appendChild(document.createTextNode(line));
+			console.appendChild(div);
+		} catch (e) {
+			try{
+				// safari needs the output wrapped in an element for some reason
+				document.write("<div>" + line + "</div>");
+			}catch(e2){
+				window.status = line;
+			}
+		}
+	}
+}
+
+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(node, evtName, fp, capture){
+	var oldHandler = node["on"+evtName] || function(){};
+	node["on"+evtName] = function(){
+		fp.apply(node, arguments);
+		oldHandler.apply(node, arguments);
+	}
+	return true;
+}
+
+
+/* Uncomment this to allow init after DOMLoad, not after window.onload
+
+// Mozilla exposes the event we could use
+if (dojo.render.html.mozilla) {
+   document.addEventListener("DOMContentLoaded", dj_load_init, null);
+}
+// for Internet Explorer. readyState will not be achieved on init call, but dojo doesn't need it
+//Tighten up the comments below to allow init after DOMLoad, not after window.onload
+/ * @cc_on @ * /
+/ * @if (@_win32)
+    document.write("<script defer>dj_load_init()<"+"/script>");
+/ * @end @ * /
+*/
+
+// default for other browsers
+// potential TODO: apply setTimeout approach for other browsers
+// that will cause flickering though ( document is loaded and THEN is processed)
+// maybe show/hide required in this case..
+// TODO: other browsers may support DOMContentLoaded/defer attribute. Add them to above.
+dj_addNodeEvtHdlr(window, "load", function(){
+	// allow multiple calls, only first one will take effect
+	if(arguments.callee.initialized){ return; }
+	arguments.callee.initialized = true;
+
+	var initFunc = function(){
+		//perform initialization
+		if(dojo.render.html.ie){
+			dojo.hostenv.makeWidgets();
+		}
+	};
+
+	if(dojo.hostenv.inFlightCount == 0){
+		initFunc();
+		dojo.hostenv.modulesLoaded();
+	}else{
+		dojo.addOnLoad(initFunc);
+	}
+});
+
+dj_addNodeEvtHdlr(window, "unload", function(){
+	dojo.hostenv.unloaded();
+});
+
+dojo.hostenv.makeWidgets = function(){
+	// you can put searchIds in djConfig and dojo.hostenv at the moment
+	// we should probably eventually move to one or the other
+	var sids = [];
+	if(djConfig.searchIds && djConfig.searchIds.length > 0) {
+		sids = sids.concat(djConfig.searchIds);
+	}
+	if(dojo.hostenv.searchIds && dojo.hostenv.searchIds.length > 0) {
+		sids = sids.concat(dojo.hostenv.searchIds);
+	}
+
+	if((djConfig.parseWidgets)||(sids.length > 0)){
+		if(dojo.evalObjPath("dojo.widget.Parse")){
+			// we must do this on a delay to avoid:
+			//	http://www.shaftek.org/blog/archives/000212.html
+			// IE is such a tremendous peice of shit.
+				var parser = new dojo.xml.Parse();
+				if(sids.length > 0){
+					for(var x=0; x<sids.length; x++){
+						var tmpNode = document.getElementById(sids[x]);
+						if(!tmpNode){ continue; }
+						var frag = parser.parseElement(tmpNode, null, true);
+						dojo.widget.getParser().createComponents(frag);
+					}
+				}else if(djConfig.parseWidgets){
+					var frag  = parser.parseElement(document.getElementsByTagName("body")[0] || document.body, null, true);
+					dojo.widget.getParser().createComponents(frag);
+				}
+		}
+	}
+}
+
+dojo.addOnLoad(function(){
+	if(!dojo.render.html.ie) {
+		dojo.hostenv.makeWidgets();
+	}
+});
+
+try {
+	if (dojo.render.html.ie) {
+		document.write('<style>v\:*{ behavior:url(#default#VML); }</style>');
+		document.write('<xml:namespace ns="urn:schemas-microsoft-com:vml" prefix="v"/>');
+	}
+} catch (e) { }
+
+// stub, over-ridden by debugging code. This will at least keep us from
+// breaking when it's not included
+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; // assume it's a node
+}


Property changes on: trunk/PhotoOp/root/static/js/src/hostenv_browser.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/hostenv_dashboard.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/hostenv_dashboard.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/hostenv_dashboard.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,197 @@
+/*
+	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.render.name = dojo.hostenv.name_ = "dashboard";
+
+dojo.hostenv.println = function(/*String*/ message){
+	// summary: Prints a message to the OS X console
+	return alert(message); // null
+}
+
+dojo.hostenv.getXmlhttpObject = function(/*Object*/ kwArgs){
+	// summary: Returns the appropriate transfer object for the call type
+	if(widget.system && kwArgs){
+		if((kwArgs.contentType && kwArgs.contentType.indexOf("text/") != 0) || (kwArgs.headers && kwArgs.headers["content-type"] && kwArgs.headers["content-type"].indexOf("text/") != 0)){
+			var curl = new dojo.hostenv.CurlRequest;
+			curl._save = true;
+			return curl;
+		}else if(kwArgs.method && kwArgs.method.toUpperCase() == "HEAD"){
+			return new dojo.hostenv.CurlRequest;
+		}else if(kwArgs.headers && kwArgs.header.referer){
+			return new dojo.hostenv.CurlRequest; 
+		}
+	}
+	return new XMLHttpRequest; // XMLHttpRequest
+}
+
+dojo.hostenv.CurlRequest = function(){
+	// summary: Emulates the XMLHttpRequest Object
+	this.onreadystatechange = null;
+	this.readyState = 0;
+	this.responseText = "";
+	this.responseXML = null;
+	this.status = 0;
+	this.statusText = "";
+	this._method = "";
+	this._url = "";
+	this._async = true;
+	this._referrer = "";
+	this._headers = [];
+	this._save = false;
+	this._responseHeader = "";
+	this._responseHeaders = {};
+	this._fileName = "";
+	this._username = "";
+	this._password = "";
+}
+
+dojo.hostenv.CurlRequest.prototype.open = function(/*String*/ method, /*URL*/ url, /*Boolean?*/ async, /*String?*/ username, /*String?*/ password){
+	this._method = method;
+	this._url = url;
+	if(async){
+		this._async = async;
+	}
+	if(username){
+		this._username = username;
+	}
+	if(password){
+		this._password = password;
+	}
+}
+
+dojo.hostenv.CurlRequest.prototype.setRequestHeader = function(/*String*/ label, /*String*/ value){
+	switch(label){
+		case "Referer":
+			this._referrer = value;
+			break;
+		case "content-type":
+			break;
+		default:
+			this._headers.push(label + "=" + value);
+			break;
+	}
+}
+
+dojo.hostenv.CurlRequest.prototype.getAllResponseHeaders = function(){
+	return this._responseHeader; // String
+}
+
+dojo.hostenv.CurlRequest.prototype.getResponseHeader = function(/*String*/ headerLabel){
+	return this._responseHeaders[headerLabel]; // String
+}
+
+// -sS = Show only errors in errorString
+// -i = Display headers with return
+// -e = Referrer URI
+// -H = Headers
+// -d = data to be sent (forces POST)
+// -G = forces GET
+// -o = Writes to file (in the cache directory)
+// -I = Only load headers
+// -u = user:password
+dojo.hostenv.CurlRequest.prototype.send = function(/*String*/ content){
+	this.readyState = 1;
+	if(this.onreadystatechange){
+		this.onreadystatechange.call(this);
+	}
+	var query = {sS: ""};
+	if(this._referrer){
+		query.e = this._referrer;
+	}
+	if(this._headers.length){
+		query.H = this._headers.join("&");
+	}
+	if(this._username){
+		if(this._password){
+			query.u = this._username + ":" + this._password;
+		}else{
+			query.u = this._username;
+		}
+	}
+	if(content){
+		query.d = this.content;
+		if(this._method != "POST"){
+			query.G = "";
+		}
+	}
+	if(this._method == "HEAD"){
+		query.I = "";
+	}else{
+		if(this._save){
+			query.I = ""; // Get the headers in the initial query
+		}else{
+			query.i = "";
+		}
+	}
+
+	var system = widget.system(dojo.hostenv.CurlRequest._formatCall(query, this._url), null);
+	this.readyState = 2;
+	if(this.onreadystatechange){
+		this.onreadystatechange.call(this);
+	}
+	if(system.errorString){
+		this.responseText = system.errorString;
+		this.status = 0;
+	}else{
+		if(this._save){
+			this._responseHeader = system.outputString;
+		}else{
+			var split = system.outputString.replace(/\r/g, "").split("\n\n", 2);
+			this._responseHeader = split[0];
+			this.responseText = split[1];
+		}
+		split = this._responseHeader.split("\n");
+		this.statusText = split.shift();
+		this.status = this.statusText.split(" ")[1];
+		for(var i = 0, header; header = split[i]; i++){
+			var header_split = header.split(": ", 2);
+			this._responseHeaders[header_split[0]] = header_split[1];
+		}
+		if(this._save){
+			widget.system("/bin/mkdir cache", null);
+			// First, make a file name
+			this._fileName = this._url.split("/").pop().replace(/\W/g, "");
+			// Then, get its extension
+			this._fileName += "." + this._responseHeaders["Content-Type"].replace(/[\r\n]/g, "").split("/").pop()
+			delete query.I;
+			query.o = "cache/" + this._fileName; // Tell it where to be saved.
+			system = widget.system(dojo.hostenv.CurlRequest._formatCall(query, this._url), null);
+			if(!system.errorString){
+				this.responseText = "cache/" + this._fileName;
+			}
+		}else if(this._method == "HEAD"){
+			this.responseText = this._responseHeader;
+		}
+	}
+
+	this.readyState = 4;
+	if(this.onreadystatechange){
+		this.onreadystatechange.call(this);
+	}
+}
+
+dojo.hostenv.CurlRequest._formatCall = function(query, url){
+	var call = ["/usr/bin/curl"];
+	for(var key in query){
+		if(query[key] != ""){
+			call.push("-" + key + " '" + query[key].replace(/'/g, "\'") + "'");
+		}else{
+			call.push("-" + key);
+		}
+	}
+	call.push("'" + url.replace(/'/g, "\'") + "'");
+	return call.join(" ");
+}
+
+dojo.hostenv.exit = function(){
+	if(widget.system){
+		widget.system("/bin/rm -rf cache/*", null);
+	}
+}


Property changes on: trunk/PhotoOp/root/static/js/src/hostenv_dashboard.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/hostenv_jsc.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/hostenv_jsc.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/hostenv_jsc.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,76 @@
+/*
+	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
+*/
+
+/*
+ * JScript .NET jsc
+ *
+ */
+
+dojo.hostenv.name_ = 'jsc';
+
+// Sanity check this is the right hostenv.
+// See the Rotor source code jscript/engine/globalobject.cs for what globals
+// are available.
+if((typeof ScriptEngineMajorVersion != 'function')||(ScriptEngineMajorVersion() < 7)){
+	dojo.raise("attempt to use JScript .NET host environment with inappropriate ScriptEngine"); 
+}
+
+// for more than you wanted to know about why this import is required even if
+// we fully qualify all symbols, see
+// http://groups.google.com/groups?th=f050c7aeefdcbde2&rnum=12
+import System;
+
+dojo.hostenv.getText = function(uri){
+	if(!System.IO.File.Exists(uri)){
+		// dojo.raise("No such file '" + uri + "'");
+		return 0;
+	}
+	var reader = new System.IO.StreamReader(uri);
+	var contents : String = reader.ReadToEnd();
+	return contents;
+}
+
+dojo.hostenv.loadUri = function(uri){
+	var contents = this.getText(uri);
+	if(!contents){
+		dojo.raise("got no back contents from uri '" + uri + "': " + contents);
+	}
+	// TODO: in JScript .NET, eval will not affect the symbol table of the current code?
+	var value = dj_eval(contents);
+	dojo.debug("jsc eval of contents returned: ", value);
+	return 1;
+
+	// for an example doing runtime code compilation, see:
+	// http://groups.google.com/groups?selm=eQ1aeciCBHA.1644%40tkmsftngp05&rnum=6
+	// Microsoft.JScript or System.CodeDom.Compiler ?
+	// var engine = new Microsoft.JScript.Vsa.VsaEngine()
+	// what about loading a js file vs. a dll?
+	// GetObject("script:" . uri);
+}
+
+/* The System.Environment object is useful:
+    print ("CommandLine='" + System.Environment.CommandLine + "' " +
+	   "program name='" + System.Environment.GetCommandLineArgs()[0] + "' " +
+	   "CurrentDirectory='" + System.Environment.CurrentDirectory + "' " +
+	   "StackTrace='" + System.Environment.StackTrace + "'");
+*/
+
+// same as System.Console.WriteLine
+// sigh; Rotor treats symbol "print" at parse time without actually putting it
+// in the builtin symbol table.
+// Note that the print symbol is not available if jsc is run with the "/print-"
+// option.
+dojo.hostenv.println = function(s){
+	print(s); // = print
+}
+
+dojo.hostenv.getLibraryScriptUri = function(){
+	return System.Environment.GetCommandLineArgs()[0];
+}


Property changes on: trunk/PhotoOp/root/static/js/src/hostenv_jsc.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/hostenv_rhino.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/hostenv_rhino.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/hostenv_rhino.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,190 @@
+/*
+	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
+*/
+
+/*
+* Rhino host environment
+*/
+
+// make jsc shut up (so we can use jsc for sanity checking) 
+/*@cc_on
+ at if (@_jscript_version >= 7)
+var loadClass; var print; var load; var quit; var version; var Packages; var java;
+ at end
+@*/
+
+// TODO: not sure what we gain from the next line, anyone?
+//if (typeof loadClass == 'undefined') { dojo.raise("attempt to use Rhino host environment when no 'loadClass' global"); }
+
+dojo.render.name = dojo.hostenv.name_ = 'rhino';
+dojo.hostenv.getVersion = function() {return version()};
+
+// see comments in spidermonkey loadUri
+dojo.hostenv.loadUri = function(uri, cb){
+	dojo.debug("uri: "+uri);
+	try{
+		// FIXME: what about remote URIs?
+		var found = true;
+		if(!(new java.io.File(uri)).exists()){
+			try{
+				// try it as a file first, URL second
+				(new java.io.URL(uri)).openStream();
+			}catch(e){
+				found = false;
+			}
+		}
+		if(!found){
+			dojo.debug(uri+" does not exist");
+			if(cb){ cb(0); }
+			return 0;
+		}
+		var ok = load(uri);
+		// dojo.debug(typeof ok);
+		dojo.debug("rhino load('", uri, "') returned. Ok: ", ok);
+		if(cb){ cb(1); }
+		return 1;
+	}catch(e){
+		dojo.debug("rhino load('", uri, "') failed");
+		if(cb){ cb(0); }
+		return 0;
+	}
+}
+
+dojo.hostenv.println = print;
+dojo.hostenv.exit = function(exitcode){ 
+	quit(exitcode);
+}
+
+// Hack to determine current script...
+//
+// These initial attempts failed:
+//   1. get an EcmaError and look at e.getSourceName(): try {eval ("static in return")} catch(e) { ...
+//   Won't work because NativeGlobal.java only does a put of "name" and "message", not a wrapped reflecting object.
+//   Even if the EcmaError object had the sourceName set.
+//  
+//   2. var e = Packages.org.mozilla.javascript.Context.getCurrentContext().reportError('');
+//   Won't work because it goes directly to the errorReporter, not the return value.
+//   We want context.interpreterSourceFile and context.interpreterLine, which are used in static Context.getSourcePositionFromStack
+//   (set by Interpreter.java at interpretation time, if in interpreter mode).
+//
+//   3. var e = Packages.org.mozilla.javascript.Context.getCurrentContext().reportRuntimeError('');
+//   This returns an object, but e.message still does not have source info.
+//   In compiler mode, perhaps not set; in interpreter mode, perhaps not used by errorReporter?
+//
+// What we found works is to do basically the same hack as is done in getSourcePositionFromStack,
+// making a new java.lang.Exception() and then calling printStackTrace on a string stream.
+// We have to parse the string for the .js files (different from the java files).
+// This only works however in compiled mode (-opt 0 or higher).
+// In interpreter mode, entire stack is java.
+// When compiled, printStackTrace is like:
+// java.lang.Exception
+//	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
+//	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
+//	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
+//	at java.lang.reflect.Constructor.newInstance(Constructor.java:274)
+//	at org.mozilla.javascript.NativeJavaClass.constructSpecific(NativeJavaClass.java:228)
+//	at org.mozilla.javascript.NativeJavaClass.construct(NativeJavaClass.java:185)
+//	at org.mozilla.javascript.ScriptRuntime.newObject(ScriptRuntime.java:1269)
+//	at org.mozilla.javascript.gen.c2.call(/Users/mda/Sites/burstproject/testrhino.js:27)
+//    ...
+//	at org.mozilla.javascript.tools.shell.Main.main(Main.java:76)
+//
+// Note may get different answers based on:
+//    Context.setOptimizationLevel(-1)
+//    Context.setGeneratingDebug(true)
+//    Context.setGeneratingSource(true) 
+//
+// Some somewhat helpful posts:
+//    http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&oe=UTF-8&safe=off&selm=9v9n0g%246gr1%40ripley.netscape.com
+//    http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&oe=UTF-8&safe=off&selm=3BAA2DC4.6010702%40atg.com
+//
+// Note that Rhino1.5R5 added source name information in some exceptions.
+// But this seems not to help in command-line Rhino, because Context.java has an error reporter
+// so no EvaluationException is thrown.
+
+// do it by using java java.lang.Exception
+function dj_rhino_current_script_via_java(depth) {
+    var optLevel = Packages.org.mozilla.javascript.Context.getCurrentContext().getOptimizationLevel();  
+    if (optLevel == -1) dojo.unimplemented("getCurrentScriptURI (determine current script path for rhino when interpreter mode)", '');
+    var caw = new java.io.CharArrayWriter();
+    var pw = new java.io.PrintWriter(caw);
+    var exc = new java.lang.Exception();
+    var s = caw.toString();
+    // we have to exclude the ones with or without line numbers because they put double entries in:
+    //   at org.mozilla.javascript.gen.c3._c4(/Users/mda/Sites/burstproject/burst/Runtime.js:56)
+    //   at org.mozilla.javascript.gen.c3.call(/Users/mda/Sites/burstproject/burst/Runtime.js)
+    var matches = s.match(/[^\(]*\.js\)/gi);
+    if(!matches){
+		throw Error("cannot parse printStackTrace output: " + s);
+	}
+
+    // matches[0] is entire string, matches[1] is this function, matches[2] is caller, ...
+    var fname = ((typeof depth != 'undefined')&&(depth)) ? matches[depth + 1] : matches[matches.length - 1];
+    var fname = matches[3];
+	if(!fname){ fname = matches[1]; }
+    // print("got fname '" + fname + "' from stack string '" + s + "'");
+    if (!fname) throw Error("could not find js file in printStackTrace output: " + s);
+    //print("Rhino getCurrentScriptURI returning '" + fname + "' from: " + s); 
+    return fname;
+}
+
+// UNUSED: leverage new support in native exception for getSourceName
+/*
+function dj_rhino_current_script_via_eval_exception() {
+    var exc;
+    // 'ReferenceError: "undefinedsymbol" is not defined.'
+    try {eval ("undefinedsymbol()") } catch(e) {exc = e;}
+    // 'Error: whatever'
+    // try{throw Error("whatever");} catch(e) {exc = e;}
+    // 'SyntaxError: identifier is a reserved word'
+    // try {eval ("static in return")} catch(e) { exc = e; }
+    print("got exception: '" + exc + "'");
+    print("exc.stack=" + (typeof exc.stack));
+    var sn = exc.getSourceName();
+    print("SourceName=" + sn);
+    return sn;
+} 
+*/
+
+// reading a file from disk in Java is a humiliating experience by any measure.
+// Lets avoid that and just get the freaking text
+function readText(uri){
+	// NOTE: we intentionally avoid handling exceptions, since the caller will
+	// want to know
+	var jf = new java.io.File(uri);
+	var sb = new java.lang.StringBuffer();
+	var input = new java.io.BufferedReader(new java.io.FileReader(jf));
+	var line = "";
+	while((line = input.readLine()) != null){
+		sb.append(line);
+		sb.append(java.lang.System.getProperty("line.separator"));
+	}
+	return sb.toString();
+}
+
+// call this now because later we may not be on the top of the stack
+if(!djConfig.libraryScriptUri.length){
+	try{
+		djConfig.libraryScriptUri = dj_rhino_current_script_via_java(1);
+	}catch(e){
+		// otherwise just fake it
+		if(djConfig["isDebug"]){
+			print("\n");
+			print("we have no idea where Dojo is located from.");
+			print("Please try loading rhino in a non-interpreted mode or set a");
+			print("\n	djConfig.libraryScriptUri\n");
+			print("Setting the dojo path to './'");
+			print("This is probably wrong!");
+			print("\n");
+			print("Dojo will try to load anyway");
+		}
+		djConfig.libraryScriptUri = "./";
+	}
+}
+


Property changes on: trunk/PhotoOp/root/static/js/src/hostenv_rhino.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/hostenv_spidermonkey.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/hostenv_spidermonkey.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/hostenv_spidermonkey.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,79 @@
+/*
+	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
+*/
+
+/*
+ * SpiderMonkey host environment
+ */
+
+dojo.hostenv.name_ = 'spidermonkey';
+
+dojo.hostenv.println = print;
+dojo.hostenv.exit = function(exitcode){ 
+	quit(exitcode); 
+}
+
+// version() returns 0, sigh. and build() returns nothing but just prints.
+dojo.hostenv.getVersion = function(){ return version(); }
+
+// make jsc shut up (so we can use jsc for sanity checking) 
+/*@cc_on
+ at if (@_jscript_version >= 7)
+var line2pc; var print; var load; var quit;
+ at end
+@*/
+
+if(typeof line2pc == 'undefined'){
+	dojo.raise("attempt to use SpiderMonkey host environment when no 'line2pc' global");
+}
+
+/*
+ * This is a hack that determines the current script file by parsing a generated
+ * stack trace (relying on the non-standard "stack" member variable of the
+ * SpiderMonkey Error object).
+ * If param depth is passed in, it'll return the script file which is that far down
+ * the stack, but that does require that you know how deep your stack is when you are
+ * calling.
+ */
+function dj_spidermonkey_current_file(depth){
+    var s = '';
+    try{
+		throw Error("whatever");
+	}catch(e){
+		s = e.stack;
+	}
+    // lines are like: bu_getCurrentScriptURI_spidermonkey("ScriptLoader.js")@burst/Runtime.js:101
+    var matches = s.match(/[^@]*\.js/gi);
+    if(!matches){ 
+		dojo.raise("could not parse stack string: '" + s + "'");
+	}
+    var fname = (typeof depth != 'undefined' && depth) ? matches[depth + 1] : matches[matches.length - 1];
+    if(!fname){ 
+		dojo.raise("could not find file name in stack string '" + s + "'");
+	}
+    //print("SpiderMonkeyRuntime got fname '" + fname + "' from stack string '" + s + "'");
+    return fname;
+}
+
+// call this now because later we may not be on the top of the stack
+if(!dojo.hostenv.library_script_uri_){ 
+	dojo.hostenv.library_script_uri_ = dj_spidermonkey_current_file(0); 
+}
+
+dojo.hostenv.loadUri = function(uri){
+	// spidermonkey load() evaluates the contents into the global scope (which
+	// is what we want).
+	// TODO: sigh, load() does not return a useful value. 
+	// Perhaps it is returning the value of the last thing evaluated?
+	var ok = load(uri);
+	// dojo.debug("spidermonkey load(", uri, ") returned ", ok);
+	return 1;
+}
+
+


Property changes on: trunk/PhotoOp/root/static/js/src/hostenv_spidermonkey.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/hostenv_svg.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/hostenv_svg.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/hostenv_svg.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,223 @@
+/*
+	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
+*/
+
+//	hostenv_svg
+if(typeof window == 'undefined'){
+	dojo.raise("attempt to use adobe svg hostenv when no window object");
+}
+dojo.debug = function(){ 
+	if (!djConfig.isDebug) { return; }
+	var args = arguments;
+	var isJUM = dj_global["jum"];
+	var s = isJUM ? "": "DEBUG: ";
+	for (var i = 0; i < args.length; ++i){ s += args[i]; }
+	if (isJUM){ // this seems to be the only way to get JUM to "play nice"
+		jum.debug(s);
+	} else{ 
+		dojo.hostenv.println(s);
+	}
+};
+
+//	set up dojo.render.
+dojo.render.name = navigator.appName;
+dojo.render.ver = parseFloat(navigator.appVersion, 10);
+switch(navigator.platform){
+	case "MacOS":
+		dojo.render.os.osx =  true;
+		break;
+	case "Linux":
+		dojo.render.os.linux =  true;
+		break;
+	case "Windows":
+		dojo.render.os.win =  true;
+		break;
+	default:
+		dojo.render.os.linux = true;
+		break;
+};
+dojo.render.svg.capable = true;
+dojo.render.svg.support.builtin = true;
+//	FIXME the following two is a big-ass hack for now.
+dojo.render.svg.moz = ((navigator.userAgent.indexOf("Gecko") >= 0) && (!((navigator.appVersion.indexOf("Konqueror") >= 0) || (navigator.appVersion.indexOf("Safari") >= 0))));
+dojo.render.svg.adobe = (window.parseXML != null);
+
+//	agent-specific implementations.
+
+//	from old hostenv_adobesvg.
+dojo.hostenv.startPackage("dojo.hostenv");
+dojo.hostenv.println = function(s){ 
+	try {
+		var ti = document.createElement("text");
+		ti.setAttribute("x","50");
+		ti.setAttribute("y", (25 + 15 * document.getElementsByTagName("text").length));
+		ti.appendChild(document.createTextNode(s));
+		document.documentElement.appendChild(ti);
+	} catch(e){ }
+};
+dojo.hostenv.name_ = "svg";
+
+//	expected/defined by bootstrap1.js
+dojo.hostenv.setModulePrefix = function(module, prefix){ };
+dojo.hostenv.getModulePrefix = function(module){ };
+dojo.hostenv.getTextStack = [];
+dojo.hostenv.loadUriStack = [];
+dojo.hostenv.loadedUris = [];
+dojo.hostenv.modules_ = {};
+dojo.hostenv.modulesLoadedFired = false;
+dojo.hostenv.modulesLoadedListeners = [];
+dojo.hostenv.getText = function(uri, cb, data){ 
+	if (!cb) var cb = function(result){ window.alert(result); };
+	if (!data) {
+		window.getUrl(uri, cb);
+	} else {
+		window.postUrl(uri, data, cb);
+	}
+};
+dojo.hostenv.getLibaryScriptUri = function(){ };
+
+dojo.hostenv.loadUri = function(uri){ };
+dojo.hostenv.loadUriAndCheck = function(uri, module){ };
+
+//	aliased in loader.js, don't ignore
+//	we are going to kill loadModule for the first round of SVG stuff, and include shit manually.
+dojo.hostenv.loadModule = function(moduleName){
+	//	just like startPackage, but this time we're just checking to make sure it exists already.
+	var a = moduleName.split(".");
+	var currentObj = window;
+	var s = [];
+	for (var i = 0; i < a.length; i++){
+		if (a[i] == "*") continue;
+		s.push(a[i]);
+		if (!currentObj[a[i]]){
+			dojo.raise("dojo.require('" + moduleName + "'): module does not exist.");
+		} else currentObj = currentObj[a[i]];
+	}
+	return; 
+};
+dojo.hostenv.startPackage = function(moduleName){
+	var a = moduleName.split(".");
+	var currentObj = window;
+	var s = [];
+	for (var i = 0; i < a.length; i++){
+		if (a[i] == "*") continue;
+		s.push(a[i]);
+		if (!currentObj[a[i]]) currentObj[a[i]] = {};
+		currentObj = currentObj[a[i]];
+	}
+	return; 
+};
+
+//	wrapper objects for ASVG
+if (window.parseXML){
+	window.XMLSerialzer = function(){
+		//	based on WebFX RichTextControl getXHTML() function.
+		function nodeToString(n, a) {
+			function fixText(s) { return String(s).replace(/\&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;"); }
+			function fixAttribute(s) { return fixText(s).replace(/\"/g, "&quot;"); }
+			switch (n.nodeType) {
+				case 1:	{	//	ELEMENT
+					var name = n.nodeName;
+					a.push("<" + name);
+					for (var i = 0; i < n.attributes.length; i++) {
+						if (n.attributes.item(i).specified) {
+							a.push(" " + n.attributes.item(i).nodeName.toLowerCase() + "=\"" + fixAttribute(n.attributes.item(i).nodeValue) + "\"");
+						}
+					}
+					if (n.canHaveChildren || n.hasChildNodes()) {
+						a.push(">");
+						for (var i = 0; i < n.childNodes.length; i++) nodeToString(n.childNodes.item(i), a);
+						a.push("</" + name + ">\n");
+					} else a.push(" />\n");
+					break;
+				}
+				case 3: {	//	TEXT
+					a.push(fixText(n.nodeValue));
+					break;
+				}
+				case 4: {	//	CDATA
+					a.push("<![CDA" + "TA[\n" + n.nodeValue + "\n]" + "]>");
+					break;
+				}
+				case 7:{	//	PROCESSING INSTRUCTION
+					a.push(n.nodeValue);
+					if (/(^<\?xml)|(^<\!DOCTYPE)/.test(n.nodeValue)) a.push("\n");
+					break;
+				}
+				case 8:{	//	COMMENT
+					a.push("<!-- " + n.nodeValue + " -->\n");
+					break;
+				}
+				case 9:		//	DOCUMENT
+				case 11:{	//	DOCUMENT FRAGMENT
+					for (var i = 0; i < n.childNodes.length; i++) nodeToString(n.childNodes.item(i), a);
+					break;
+				}
+				default:{
+					a.push("<!--\nNot Supported:\n\n" + "nodeType: " + n.nodeType + "\nnodeName: " + n.nodeName + "\n-->");
+				}
+			}
+		}
+		this.serializeToString = function(node){
+			var a = [];
+			nodeToString(node, a);
+			return a.join("");
+		};
+	};
+
+	window.DOMParser = function(){
+		//	mimetype is basically ignored
+		this.parseFromString = function(s){
+			return parseXML(s, window.document);
+		}
+	};
+
+	window.XMLHttpRequest = function(){
+		//	we ignore the setting and getting of content-type.
+		var uri = null;
+		var method = "POST";
+		var isAsync = true;	
+		var cb = function(d){
+			this.responseText = d.content;
+			try {
+				this.responseXML = parseXML(this.responseText, window.document);
+			} catch(e){}
+			this.status = "200";
+			this.statusText = "OK";
+			if (!d.success) {
+				this.status = "500";
+				this.statusText = "Internal Server Error";
+			}
+			this.onload();
+			this.onreadystatechange();
+		};
+		this.onload = function(){};
+		this.readyState = 4;
+		this.onreadystatechange = function(){};
+		this.status = 0;
+		this.statusText = "";
+		this.responseBody = null;
+		this.responseStream = null;
+		this.responseXML = null;
+		this.responseText = null;
+		this.abort = function(){ return; };
+		this.getAllResponseHeaders = function(){ return []; };
+		this.getResponseHeader = function(n){ return null; };
+		this.setRequestHeader = function(nm, val){ };
+		this.open = function(meth, url, async){ 
+			method = meth;
+			uri = url;
+		};
+		this.send = function(data){
+			var d = data || null;
+			if (method == "GET") getURL(uri, cb);
+			else postURL(uri, data, cb);
+		};
+	};
+}


Property changes on: trunk/PhotoOp/root/static/js/src/hostenv_svg.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/hostenv_wsh.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/hostenv_wsh.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/hostenv_wsh.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,46 @@
+/*
+	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
+*/
+
+/*
+ * WSH
+ */
+
+dojo.hostenv.name_ = 'wsh';
+
+// make jsc shut up (so can sanity check)
+/*@cc_on
+ at if (@_jscript_version >= 7)
+var WScript;
+ at end
+@*/
+
+// make sure we are in right environment
+if(typeof WScript == 'undefined'){
+	dojo.raise("attempt to use WSH host environment when no WScript global");
+}
+
+dojo.hostenv.println = WScript.Echo;
+
+dojo.hostenv.getCurrentScriptUri = function(){
+	return WScript.ScriptFullName();
+}
+
+dojo.hostenv.getText = function(fpath){
+	var fso = new ActiveXObject("Scripting.FileSystemObject");
+	var istream = fso.OpenTextFile(fpath, 1); // iomode==1 means read only
+	if(!istream){
+		return null;
+	}
+	var contents = istream.ReadAll();
+	istream.Close();
+	return contents;
+}
+
+dojo.hostenv.exit = function(exitcode){ WScript.Quit(exitcode); }


Property changes on: trunk/PhotoOp/root/static/js/src/hostenv_wsh.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/html/__package__.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/html/__package__.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/html/__package__.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,14 @@
+/*
+	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.kwCompoundRequire({
+	common: ["dojo.html", "dojo.html.extras", "dojo.html.shadow"]
+});
+dojo.provide("dojo.html.*");


Property changes on: trunk/PhotoOp/root/static/js/src/html/__package__.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/html/extras.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/html/extras.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/html/extras.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -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.
+ * <p>
+ * If you wanted to insert a node into a DOM tree based on the mouse
+ * position you might use the following code:
+ * <pre>
+ * if (gravity(node, e) & gravity.NORTH) { [insert before]; }
+ * else { [insert after]; }
+ * </pre>
+ *
+ * @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((/^<t[dh][\s\r\n>]/i).test(dojo.string.trimStart(txt))) {
+		txt = "<table><tbody><tr>" + txt + "</tr></tbody></table>";
+		tableType = "cell";
+	} else if((/^<tr[\s\r\n>]/i).test(dojo.string.trimStart(txt))) {
+		txt = "<table><tbody>" + txt + "</tbody></table>";
+		tableType = "row";
+	} else if((/^<(thead|tbody|tfoot)[\s\r\n>]/i).test(dojo.string.trimStart(txt))) {
+		txt = "<table>" + txt + "</table>";
+		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<parent.childNodes.length; x++){
+		nodes.push(parent.childNodes[x].cloneNode(true));
+	}
+	tn.style.display = "none"; // FIXME: why do we do this?
+	document.body.removeChild(tn);
+	return nodes;
+}
+
+/* TODO: merge placeOnScreen and placeOnScreenPoint to make 1 function that allows you
+ * to define which corner(s) you want to bind to. Something like so:
+ *
+ * kes(node, desiredX, desiredY, "TR")
+ * kes(node, [desiredX, desiredY], ["TR", "BL"])
+ *
+ * TODO: make this function have variable call sigs
+ *
+ * kes(node, ptArray, cornerArray, padding, hasScroll)
+ * kes(node, ptX, ptY, cornerA, cornerB, cornerC, paddingArray, hasScroll)
+ */
+
+/**
+ * Keeps 'node' in the visible area of the screen while trying to
+ * place closest to desiredX, desiredY. The input coordinates are
+ * expected to be the desired screen position, not accounting for
+ * scrolling. If you already accounted for scrolling, set 'hasScroll'
+ * to true. Set padding to either a number or array for [paddingX, paddingY]
+ * to put some buffer around the element you want to position.
+ * NOTE: node is assumed to be absolutely or relatively positioned.
+ *
+ * Alternate call sig:
+ *  placeOnScreen(node, [x, y], padding, hasScroll)
+ *
+ * Examples:
+ *  placeOnScreen(node, 100, 200)
+ *  placeOnScreen("myId", [800, 623], 5)
+ *  placeOnScreen(node, 234, 3284, [2, 5], true)
+ */
+dojo.html.placeOnScreen = 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 w = node.offsetWidth + padding[0];
+	var h = node.offsetHeight + padding[1];
+
+	if(hasScroll) {
+		desiredX -= scroll.x;
+		desiredY -= scroll.y;
+	}
+
+	var x = desiredX + w;
+	if(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=
+				 "<iframe "
+				+"style='position: absolute; left: 0px; top: 0px; width: 100%; height: 100%;"
+				+        "z-index: -1; filter:Alpha(Opacity=\"0\");' "
+				+">";
+		this.iframe = document.createElement(html);
+		if(node){
+			node.appendChild(this.iframe);
+			this.domNode=node;
+		}else{
+			document.body.appendChild(this.iframe);
+			this.iframe.style.display="none";
+		}
+	}
+}
+dojo.lang.extend(dojo.html.BackgroundIframe, {
+	iframe: null,
+
+	// TODO: this function shouldn't be necessary but setting width=height=100% doesn't work!
+	onResized: function(){
+		if(this.iframe && this.domNode && this.domNode.parentElement){ // No parentElement if onResized() timeout event occurs on a removed domnode
+			var w = dojo.style.getOuterWidth(this.domNode);
+			var h = dojo.style.getOuterHeight(this.domNode);
+			if (w  == 0 || h == 0 ){
+				dojo.lang.setTimeout(this, this.onResized, 50);
+				return;
+			}
+			var s = this.iframe.style;
+			s.width = w + "px";
+			s.height = h + "px";
+		}
+	},
+
+	// Call this function if the iframe is connected to document.body rather
+	// than the node being shadowed (TODO: erase)
+	size: function(node) {
+		if(!this.iframe) { return; }
+
+		var coords = dojo.style.toCoordinateArray(node, true);
+
+		var s = this.iframe.style;
+		s.width = coords.w + "px";
+		s.height = coords.h + "px";
+		s.left = coords.x + "px";
+		s.top = coords.y + "px";
+	},
+
+	setZIndex: function(node /* or number */) {
+		if(!this.iframe) { return; }
+
+		if(dojo.dom.isNode(node)) {
+			this.iframe.style.zIndex = dojo.html.getStyle(node, "z-index") - 1;
+		} else if(!isNaN(node)) {
+			this.iframe.style.zIndex = node;
+		}
+	},
+
+	show: function() {
+		if(!this.iframe) { return; }
+		this.iframe.style.display = "block";
+	},
+
+	hide: function() {
+		if(!this.ie) { return; }
+		var s = this.iframe.style;
+		s.display = "none";
+	},
+
+	remove: function() {
+		dojo.dom.removeNode(this.iframe);
+	}
+});


Property changes on: trunk/PhotoOp/root/static/js/src/html/extras.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/html/images/shadowB.png
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/html/images/shadowB.png
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/html/images/shadowBL.png
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/html/images/shadowBL.png
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/html/images/shadowBR.png
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/html/images/shadowBR.png
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/html/images/shadowL.png
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/html/images/shadowL.png
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/html/images/shadowR.png
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/html/images/shadowR.png
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/html/images/shadowT.png
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/html/images/shadowT.png
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/html/images/shadowTL.png
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/html/images/shadowTL.png
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/html/images/shadowTR..png
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/html/images/shadowTR..png
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/html/images/shadowTR.png
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/html/images/shadowTR.png
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/html/layout.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/html/layout.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/html/layout.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,121 @@
+/*
+	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.html.layout");
+
+dojo.require("dojo.lang");
+dojo.require("dojo.string");
+dojo.require("dojo.style");
+dojo.require("dojo.html");
+
+/**
+ * Layout a bunch of child dom nodes within a parent dom node
+ * Input is an array of objects like:
+ * @ container - parent node
+ * @ layoutPriority - "top-bottom" or "left-right"
+ * @ children an array like [ {domNode: foo, layoutAlign: "bottom" }, {domNode: bar, layoutAlign: "client"} ]
+ */
+dojo.html.layout = function(container, children, layoutPriority) {
+	dojo.html.addClass(container, "dojoLayoutContainer");
+
+	// Copy children array and remove elements w/out layout.
+	// Also record each child's position in the input array, for sorting purposes.
+	children = dojo.lang.filter(children, function(child, idx){
+		child.idx = idx;
+		return dojo.lang.inArray(["top","bottom","left","right","client","flood"], child.layoutAlign)
+	});
+
+	// Order the children according to layoutPriority.
+	// Multiple children w/the same layoutPriority will be sorted by their position in the input array.
+	if(layoutPriority && layoutPriority!="none"){
+		var rank = function(child){
+			switch(child.layoutAlign){
+				case "flood":
+					return 1;
+				case "left":
+				case "right":
+					return (layoutPriority=="left-right") ? 2 : 3;
+				case "top":
+				case "bottom":
+					return (layoutPriority=="left-right") ? 3 : 2;
+				default:
+					return 4;
+			}
+		};
+		children.sort(function(a,b){
+			return (rank(a)-rank(b)) || (a.idx - b.idx);
+		});
+	}
+
+	// remaining space (blank area where nothing has been written)
+	var f={
+		top: dojo.style.getPixelValue(container, "padding-top", true),
+		left: dojo.style.getPixelValue(container, "padding-left", true),
+		height: dojo.style.getContentHeight(container),
+		width: dojo.style.getContentWidth(container)
+	};
+
+	// set positions/sizes
+	dojo.lang.forEach(children, function(child){
+		var elm=child.domNode;
+		var pos=child.layoutAlign;
+		// set elem to upper left corner of unused space; may move it later
+		with(elm.style){
+			left = f.left+"px";
+			top = f.top+"px";
+			bottom = "auto";
+			right = "auto";
+		}
+		dojo.html.addClass(elm, "dojoAlign" + dojo.string.capitalize(pos));
+
+		// set size && adjust record of remaining space.
+		// note that setting the width of a <div> may affect it's height.
+		// TODO: same is true for widgets but need to implement API to support that
+		if ( (pos=="top")||(pos=="bottom") ) {
+			dojo.style.setOuterWidth(elm, f.width);
+			var h = dojo.style.getOuterHeight(elm);
+			f.height -= h;
+			if(pos=="top"){
+				f.top += h;
+			}else{
+				elm.style.top = f.top + f.height + "px";
+			}
+		}else if(pos=="left" || pos=="right"){
+			dojo.style.setOuterHeight(elm, f.height);
+			var w = dojo.style.getOuterWidth(elm);
+			f.width -= w;
+			if(pos=="left"){
+				f.left += w;
+			}else{
+				elm.style.left = f.left + f.width + "px";
+			}
+		} else if(pos=="flood" || pos=="client"){
+			dojo.style.setOuterWidth(elm, f.width);
+			dojo.style.setOuterHeight(elm, f.height);
+		}
+		
+		// TODO: for widgets I want to call resizeTo(), but for top/bottom
+		// alignment I only want to set the width, and have the size determined
+		// dynamically.  (The thinner you make a div, the more height it consumes.)
+		if(child.onResized){
+			child.onResized();
+		}
+	});
+};
+
+// This is essential CSS to make layout work (it isn't "styling" CSS)
+// make sure that the position:absolute in dojoAlign* overrides other classes
+dojo.style.insertCssText(
+	".dojoLayoutContainer{ position: relative; display: block; }\n" +
+	"body .dojoAlignTop, body .dojoAlignBottom, body .dojoAlignLeft, body .dojoAlignRight { position: absolute; overflow: hidden; }\n" +
+	"body .dojoAlignClient { position: absolute }\n" +
+	".dojoAlignClient { overflow: auto; }\n"
+);
+


Property changes on: trunk/PhotoOp/root/static/js/src/html/layout.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/html/shadow.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/html/shadow.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/html/shadow.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,79 @@
+/*
+	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.html.shadow");
+
+dojo.require("dojo.lang");
+dojo.require("dojo.uri");
+
+dojo.html.shadow = function(node) {
+	this.init(node);
+}
+
+dojo.lang.extend(dojo.html.shadow, {
+
+	shadowPng: dojo.uri.dojoUri("src/html/images/shadow"),
+	shadowThickness: 8,
+	shadowOffset: 15,
+
+	init: function(node){
+		this.node=node;
+
+		// make all the pieces of the shadow, and position/size them as much
+		// as possible (but a lot of the coordinates are set in sizeShadow
+		this.pieces={};
+		var x1 = -1 * this.shadowThickness;
+		var y0 = this.shadowOffset;
+		var y1 = this.shadowOffset + this.shadowThickness;
+		this._makePiece("tl", "top", y0, "left", x1);
+		this._makePiece("l", "top", y1, "left", x1, "scale");
+		this._makePiece("tr", "top", y0, "left", 0);
+		this._makePiece("r", "top", y1, "left", 0, "scale");
+		this._makePiece("bl", "top", 0, "left", x1);
+		this._makePiece("b", "top", 0, "left", 0, "crop");
+		this._makePiece("br", "top", 0, "left", 0);
+	},
+
+	_makePiece: function(name, vertAttach, vertCoord, horzAttach, horzCoord, sizing){
+		var img;
+		var url = this.shadowPng + name.toUpperCase() + ".png";
+		if(dojo.render.html.ie){
+			img=document.createElement("div");
+			img.style.filter="progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+url+"'"+
+			(sizing?", sizingMethod='"+sizing+"'":"") + ")";
+		}else{
+			img=document.createElement("img");
+			img.src=url;
+		}
+		img.style.position="absolute";
+		img.style[vertAttach]=vertCoord+"px";
+		img.style[horzAttach]=horzCoord+"px";
+		img.style.width=this.shadowThickness+"px";
+		img.style.height=this.shadowThickness+"px";
+		this.pieces[name]=img;
+		this.node.appendChild(img);
+	},
+
+	size: function(width, height){
+		var sideHeight = height - (this.shadowOffset+this.shadowThickness+1);
+		with(this.pieces){
+			l.style.height = sideHeight+"px";
+			r.style.height = sideHeight+"px";
+			b.style.width = (width-1)+"px";
+			bl.style.top = (height-1)+"px";
+			b.style.top = (height-1)+"px";
+			br.style.top = (height-1)+"px";
+			tr.style.left = (width-1)+"px";
+			r.style.left = (width-1)+"px";
+			br.style.left = (width-1)+"px";
+		}
+	}
+});
+


Property changes on: trunk/PhotoOp/root/static/js/src/html/shadow.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/html.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/html.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/html.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,595 @@
+/*
+	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.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);
+
+// FIXME: we are going to assume that we can throw any and every rendering
+// engine into the IE 5.x box model. In Mozilla, we do this w/ CSS.
+// Need to investigate for KHTML and Opera
+
+dojo.html.clearSelection = function(){
+	try{
+		if(window["getSelection"]){ 
+			if(dojo.render.html.safari){
+				// pulled from WebCore/ecma/kjs_window.cpp, line 2536
+				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(element){
+	element = dojo.byId(element)||document.body;
+	var h = dojo.render.html;
+	
+	if(h.mozilla){
+		element.style.MozUserSelect = "none";
+	}else if(h.safari){
+		element.style.KhtmlUserSelect = "none"; 
+	}else if(h.ie){
+		element.unselectable = "on";
+	}else{
+		return false;
+	}
+	return true;
+}
+
+dojo.html.enableSelection = function(element){
+	element = dojo.byId(element)||document.body;
+	
+	var h = dojo.render.html;
+	if(h.mozilla){ 
+		element.style.MozUserSelect = ""; 
+	}else if(h.safari){
+		element.style.KhtmlUserSelect = "";
+	}else if(h.ie){
+		element.unselectable = "off";
+	}else{
+		return false;
+	}
+	return true;
+}
+
+dojo.html.selectElement = function(element){
+	element = dojo.byId(element);
+	if(document.selection && document.body.createTextRange){ // IE
+		var range = document.body.createTextRange();
+		range.moveToElementText(element);
+		range.select();
+	}else if(window["getSelection"]){
+		var selection = window.getSelection();
+		// FIXME: does this work on Safari?
+		if(selection["selectAllChildren"]){ // Mozilla
+			selection.selectAllChildren(element);
+		}
+	}
+}
+
+dojo.html.selectInputText = function(element){
+	element = dojo.byId(element);
+	if(document.selection && document.body.createTextRange){ // IE
+		var range = element.createTextRange();
+		range.moveStart("character", 0);
+		range.moveEnd("character", element.value.length);
+		range.select();
+	}else if(window["getSelection"]){
+		var selection = window.getSelection();
+		// FIXME: does this work on Safari?
+		element.setSelectionRange(0, element.value.length);
+	}
+	element.focus();
+}
+
+
+dojo.html.isSelectionCollapsed = function(){
+	if(document["selection"]){ // IE
+		return document.selection.createRange().text == "";
+	}else if(window["getSelection"]){
+		var selection = window.getSelection();
+		if(dojo.lang.isString(selection)){ // Safari
+			return selection == "";
+		}else{ // Mozilla/W3
+			return selection.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")){
+		// IE6 Strict
+		var w2 = document.documentElement.clientWidth;
+		// this lets us account for scrollbars
+		if(!w || w2 && w2 < w) {
+			w = w2;
+		}
+		return w;
+	}
+
+	if(document.body){
+		// IE
+		return document.body.clientWidth;
+	}
+
+	return 0;
+}
+
+dojo.html.getViewportHeight = function(){
+	if (window.innerHeight){
+		return window.innerHeight;
+	}
+
+	if (dojo.exists(document, "documentElement.clientHeight")){
+		// IE6 Strict
+		return document.documentElement.clientHeight;
+	}
+
+	if (document.body){
+		// IE
+		return document.body.clientHeight;
+	}
+
+	return 0;
+}
+
+dojo.html.getViewportSize = function(){
+	var ret = [dojo.html.getViewportWidth(), dojo.html.getViewportHeight()];
+	ret.w = ret[0];
+	ret.h = ret[1];
+	return ret;
+}
+
+dojo.html.getScrollTop = function(){
+	return window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
+}
+
+dojo.html.getScrollLeft = function(){
+	return window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft || 0;
+}
+
+dojo.html.getScrollOffset = function(){
+	var off = [dojo.html.getScrollLeft(), dojo.html.getScrollTop()];
+	off.x = off[0];
+	off.y = off[1];
+	return off;
+}
+
+dojo.html.getParentOfType = function(node, type){
+	dojo.deprecated("dojo.html.getParentOfType", "replaced by dojo.html.getParentByType*", "0.4");
+	return dojo.html.getParentByType(node, type);
+}
+
+dojo.html.getParentByType = function(node, type) {
+	var parent = dojo.byId(node);
+	type = type.toLowerCase();
+	while((parent)&&(parent.nodeName.toLowerCase()!=type)){
+		if(parent==(document["body"]||document["documentElement"])){
+			return null;
+		}
+		parent = parent.parentNode;
+	}
+	return parent;
+}
+
+// RAR: this function comes from nwidgets and is more-or-less unmodified.
+// We should probably look ant Burst and f(m)'s equivalents
+dojo.html.getAttribute = function(node, attr){
+	node = dojo.byId(node);
+	// FIXME: need to add support for attr-specific accessors
+	if((!node)||(!node.getAttribute)){
+		// if(attr !== 'nwType'){
+		//	alert("getAttr of '" + attr + "' with bad node"); 
+		// }
+		return null;
+	}
+	var ta = typeof attr == 'string' ? attr : new String(attr);
+
+	// first try the approach most likely to succeed
+	var v = node.getAttribute(ta.toUpperCase());
+	if((v)&&(typeof v == 'string')&&(v!="")){ return v; }
+
+	// try returning the attributes value, if we couldn't get it as a string
+	if(v && v.value){ return v.value; }
+
+	// this should work on Opera 7, but it's a little on the crashy side
+	if((node.getAttributeNode)&&(node.getAttributeNode(ta))){
+		return (node.getAttributeNode(ta)).value;
+	}else if(node.getAttribute(ta)){
+		return node.getAttribute(ta);
+	}else if(node.getAttribute(ta.toLowerCase())){
+		return node.getAttribute(ta.toLowerCase());
+	}
+	return null;
+}
+	
+/**
+ *	Determines whether or not the specified node carries a value for the
+ *	attribute in question.
+ */
+dojo.html.hasAttribute = function(node, attr){
+	node = dojo.byId(node);
+	return dojo.html.getAttribute(node, attr) ? true : false;
+}
+	
+/**
+ * Returns the string value of the list of CSS classes currently assigned
+ * directly to the node in question. Returns an empty string if no class attribute
+ * is found;
+ */
+dojo.html.getClass = function(node){
+	node = dojo.byId(node);
+	if(!node){ return ""; }
+	var cs = "";
+	if(node.className){
+		cs = node.className;
+	}else if(dojo.html.hasAttribute(node, "class")){
+		cs = dojo.html.getAttribute(node, "class");
+	}
+	return dojo.string.trim(cs);
+}
+
+/**
+ * Returns an array of CSS classes currently assigned
+ * directly to the node in question. Returns an empty array if no classes
+ * are found;
+ */
+dojo.html.getClasses = function(node) {
+	var c = dojo.html.getClass(node);
+	return (c == "") ? [] : c.split(/\s+/g);
+}
+
+/**
+ * Returns whether or not the specified classname is a portion of the
+ * class list currently applied to the node. Does not cover cascaded
+ * styles, only classes directly applied to the node.
+ */
+dojo.html.hasClass = function(node, classname){
+	return dojo.lang.inArray(dojo.html.getClasses(node), classname);
+}
+
+/**
+ * Adds the specified class to the beginning of the class list on the
+ * passed node. This gives the specified class the highest precidence
+ * when style cascading is calculated for the node. Returns true or
+ * false; indicating success or failure of the operation, respectively.
+ */
+dojo.html.prependClass = function(node, classStr){
+	classStr += " " + dojo.html.getClass(node);
+	return dojo.html.setClass(node, classStr);
+}
+
+/**
+ * Adds the specified class to the end of the class list on the
+ *	passed &node;. Returns &true; or &false; indicating success or failure.
+ */
+dojo.html.addClass = function(node, classStr){
+	if (dojo.html.hasClass(node, classStr)) {
+	  return false;
+	}
+	classStr = dojo.string.trim(dojo.html.getClass(node) + " " + classStr);
+	return dojo.html.setClass(node, classStr);
+}
+
+/**
+ *	Clobbers the existing list of classes for the node, replacing it with
+ *	the list given in the 2nd argument. Returns true or false
+ *	indicating success or failure.
+ */
+dojo.html.setClass = function(node, classStr){
+	node = dojo.byId(node);
+	var cs = new String(classStr);
+	try{
+		if(typeof node.className == "string"){
+			node.className = cs;
+		}else if(node.setAttribute){
+			node.setAttribute("class", classStr);
+			node.className = cs;
+		}else{
+			return false;
+		}
+	}catch(e){
+		dojo.debug("dojo.html.setClass() failed", e);
+	}
+	return true;
+}
+
+/**
+ * Removes the className from the node;. Returns
+ * true or false indicating success or failure.
+ */ 
+dojo.html.removeClass = function(node, classStr, allowPartialMatches){
+	var classStr = dojo.string.trim(new String(classStr));
+
+	try{
+		var cs = dojo.html.getClasses(node);
+		var nca	= [];
+		if(allowPartialMatches){
+			for(var i = 0; i<cs.length; i++){
+				if(cs[i].indexOf(classStr) == -1){ 
+					nca.push(cs[i]);
+				}
+			}
+		}else{
+			for(var i=0; i<cs.length; i++){
+				if(cs[i] != classStr){ 
+					nca.push(cs[i]);
+				}
+			}
+		}
+		dojo.html.setClass(node, nca.join(" "));
+	}catch(e){
+		dojo.debug("dojo.html.removeClass() failed", e);
+	}
+
+	return true;
+}
+
+/**
+ * Replaces 'oldClass' and adds 'newClass' to node
+ */
+dojo.html.replaceClass = function(node, newClass, oldClass) {
+	dojo.html.removeClass(node, oldClass);
+	dojo.html.addClass(node, newClass);
+}
+
+// Enum type for getElementsByClass classMatchType arg:
+dojo.html.classMatchType = {
+	ContainsAll : 0, // all of the classes are part of the node's class (default)
+	ContainsAny : 1, // any of the classes are part of the node's class
+	IsOnly : 2 // only all of the classes are part of the node's class
+}
+
+
+/**
+ * Returns an array of nodes for the given classStr, children of a
+ * parent, and optionally of a certain nodeType
+ */
+dojo.html.getElementsByClass = function(classStr, parent, nodeType, classMatchType, useNonXpath){
+	parent = dojo.byId(parent) || document;
+	var classes = classStr.split(/\s+/g);
+	var nodes = [];
+	if( classMatchType != 1 && classMatchType != 2 ) classMatchType = 0; // make it enum
+	var reClass = new RegExp("(\\s|^)((" + classes.join(")|(") + "))(\\s|$)");
+	var candidateNodes = [];
+	
+	if(!useNonXpath && document.evaluate) { // supports dom 3 xpath
+		var xpath = "//" + (nodeType || "*") + "[contains(";
+		if(classMatchType != dojo.html.classMatchType.ContainsAny){
+			xpath += "concat(' ', at class,' '), ' " +
+			classes.join(" ') and contains(concat(' ', at class,' '), ' ") +
+			" ')]";
+		}else{
+			xpath += "concat(' ', at class,' '), ' " +
+			classes.join(" ')) or contains(concat(' ', at class,' '), ' ") +
+			" ')]";
+		}
+		var xpathResult = document.evaluate(xpath, parent, null, XPathResult.ANY_TYPE, null);
+		var result = xpathResult.iterateNext();
+		while(result){
+			try{
+				candidateNodes.push(result);
+				result = xpathResult.iterateNext();
+			}catch(e){ break; }
+		}
+		return candidateNodes;
+	}else{
+		if(!nodeType){
+			nodeType = "*";
+		}
+		candidateNodes = parent.getElementsByTagName(nodeType);
+
+		var node, i = 0;
+		outer:
+		while(node = candidateNodes[i++]){
+			var nodeClasses = dojo.html.getClasses(node);
+			if(nodeClasses.length == 0){ continue outer; }
+			var matches = 0;
+	
+			for(var j = 0; j < nodeClasses.length; j++){
+				if(reClass.test(nodeClasses[j])){
+					if(classMatchType == dojo.html.classMatchType.ContainsAny){
+						nodes.push(node);
+						continue outer;
+					}else{
+						matches++;
+					}
+				}else{
+					if(classMatchType == dojo.html.classMatchType.IsOnly){
+						continue outer;
+					}
+				}
+			}
+	
+			if(matches == classes.length){
+				if(	(classMatchType == dojo.html.classMatchType.IsOnly)&&
+					(matches == nodeClasses.length)){
+					nodes.push(node);
+				}else if(classMatchType == dojo.html.classMatchType.ContainsAll){
+					nodes.push(node);
+				}
+			}
+		}
+		return nodes;
+	}
+}
+
+dojo.html.getElementsByClassName = dojo.html.getElementsByClass;
+
+/**
+ * Returns the mouse position relative to the document (not the viewport).
+ * For example, if you have a document that is 10000px tall,
+ * but your browser window is only 100px tall,
+ * if you scroll to the bottom of the document and call this function it
+ * will return {x: 0, y: 10000}
+ */
+dojo.html.getCursorPosition = function(e){
+	e = e || window.event;
+	var cursor = {x:0, y:0};
+	if(e.pageX || e.pageY){
+		cursor.x = e.pageX;
+		cursor.y = e.pageY;
+	}else{
+		var de = document.documentElement;
+		var db = document.body;
+		cursor.x = e.clientX + ((de||db)["scrollLeft"]) - ((de||db)["clientLeft"]);
+		cursor.y = e.clientY + ((de||db)["scrollTop"]) - ((de||db)["clientTop"]);
+	}
+	return cursor;
+}
+
+dojo.html.overElement = function(element, e){
+	element = dojo.byId(element);
+	var mouse = dojo.html.getCursorPosition(e);
+
+	with(dojo.html){
+		var top = getAbsoluteY(element, true);
+		var bottom = top + getInnerHeight(element);
+		var left = getAbsoluteX(element, true);
+		var right = left + getInnerWidth(element);
+	}
+	
+	return (mouse.x >= left && mouse.x <= right &&
+		mouse.y >= top && mouse.y <= bottom);
+}
+
+dojo.html.setActiveStyleSheet = function(title){
+	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") == title) { 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(){
+	// Note: document.body is not defined for a strict xhtml document
+	return document.body || document.getElementsByTagName("body")[0];
+}
+
+/**
+ * Like dojo.dom.isTag, except case-insensitive
+**/
+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(target, source){
+	// work around for opera which doesn't have cssText, and for IE which fails on setAttribute 
+	if(dojo.lang.isUndefined(source.style.cssText)){ 
+		target.setAttribute("style", source.getAttribute("style")); 
+	}else{
+		target.style.cssText = source.style.cssText; 
+	}
+	dojo.html.addClass(target, dojo.html.getClass(source));
+}
+
+dojo.html._callExtrasDeprecated = function(inFunc, args) {
+	var module = "dojo.html.extras";
+	dojo.deprecated("dojo.html." + inFunc, "moved to " + module, "0.4");
+	dojo["require"](module); // weird syntax to fool list-profile-deps (build)
+	return dojo.html[inFunc].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);
+}


Property changes on: trunk/PhotoOp/root/static/js/src/html.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/i18n/common.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/i18n/common.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/i18n/common.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,60 @@
+/*
+	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.i18n.common");
+dojo.require("dojo.lang");
+
+dojo.require("dojo.experimental");
+dojo.experimental("dojo.i18n.common");
+
+
+/**
+ * Gets a reference to a hash containing the localization for a given bundle in a package, matching the specified
+ * locale.  Bundle must have already been loaded by dojo.requireLocalization() or by a build optimization step.
+ *
+ * @param modulename package in which the bundle is found
+ * @param bundlename the filename in the directory structure without the ".js" suffix
+ * @param locale the variant to load (optional).  By default, the locale defined by the
+ *   host environment: dojo.locale
+ * @return a hash containing name/value pairs.  Throws an exception if the bundle is not found.
+ */
+dojo.i18n.getLocalization = function(modulename, bundlename, locale /*optional*/){
+	locale = dojo.normalizeLocale(locale);
+
+	// look for nearest locale match
+	var elements = locale.split('-');
+	var bundle = dojo.hostenv.findModule([modulename,"_nls",bundlename].join('.'), true);
+
+	for(var i = elements.length; i > 0; i--){
+		var loc = elements.slice(0, i).join('-');
+		if(bundle[loc]){
+			return bundle[loc];
+		}
+	}
+
+	if(bundle[dojo.fallback_locale]){
+		return bundle[dojo.fallback_locale];
+	}
+
+	dojo.raise("Bundle not found " + [modulename,"_nls",bundlename,locale].join('.'));
+};
+
+/**
+ * Is the language read left-to-right?  Most exceptions are for middle eastern languages.
+ *
+ * @param locale a string representing the locale.  By default, the locale defined by the
+ *   host environment: dojo.locale
+ * @return true if language is read left to right; false otherwise
+ */
+dojo.i18n.isLTR = function(locale /*optional*/){
+	var lang = dojo.normalizeLocale(locale).split('-')[0];
+	var RTL = {ar:true,fa:true,he:true,ur:true,yi:true};
+	return !RTL[lang];
+}


Property changes on: trunk/PhotoOp/root/static/js/src/i18n/common.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/iCalendar.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/iCalendar.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/iCalendar.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,814 @@
+/*
+	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.iCalendar");
+dojo.require("dojo.text.textDirectory");
+dojo.require("dojo.date");
+dojo.require("dojo.lang");
+
+
+dojo.iCalendar.fromText =  function (/* string */text) {
+	// summary
+	// Parse text of an iCalendar and return an array of iCalendar objects
+
+	var properties = dojo.textDirectoryTokeniser.tokenise(text);
+	var calendars = [];
+
+	//dojo.debug("Parsing iCal String");
+	for (var i = 0, begun = false; i < properties.length; i++) {
+		var prop = properties[i];
+		if (!begun) {
+			if (prop.name == 'BEGIN' && prop.value == 'VCALENDAR') {
+				begun = true;
+				var calbody = [];
+			}
+		} else if (prop.name == 'END' && prop.value == 'VCALENDAR') {
+			calendars.push(new dojo.iCalendar.VCalendar(calbody));
+			begun = false;
+		} else {
+			calbody.push(prop);
+		}
+	}
+	return /* array */calendars;
+}
+
+
+dojo.iCalendar.Component = function (/* string */ body ) {
+	// summary
+	// A component is the basic container of all this stuff. 
+
+	if (!this.name) {
+		this.name = "COMPONENT"
+	}
+
+	this.properties = [];
+	this.components = [];
+
+	if (body) {
+		for (var i = 0, context = ''; i < body.length; i++) {
+			if (context == '') {
+				if (body[i].name == 'BEGIN') {
+					context = body[i].value;
+					var childprops = [];
+				} else {
+					this.addProperty(new dojo.iCalendar.Property(body[i]));
+				}
+			} else if (body[i].name == 'END' && body[i].value == context) {
+				if (context=="VEVENT") {
+					this.addComponent(new dojo.iCalendar.VEvent(childprops));
+				} else if (context=="VTIMEZONE") {
+					this.addComponent(new dojo.iCalendar.VTimeZone(childprops));
+				} else if (context=="VTODO") {
+					this.addComponent(new dojo.iCalendar.VTodo(childprops));
+				} else if (context=="VJOURNAL") {
+					this.addComponent(new dojo.iCalendar.VJournal(childprops));
+				} else if (context=="VFREEBUSY") {
+					this.addComponent(new dojo.iCalendar.VFreeBusy(childprops));
+				} else if (context=="STANDARD") {
+					this.addComponent(new dojo.iCalendar.Standard(childprops));
+				} else if (context=="DAYLIGHT") {
+					this.addComponent(new dojo.iCalendar.Daylight(childprops));
+				} else if (context=="VALARM") {
+					this.addComponent(new dojo.iCalendar.VAlarm(childprops));
+				}else {
+					dojo.unimplemented("dojo.iCalendar." + context);
+				}
+				context = '';
+			} else {
+				childprops.push(body[i]);
+			}
+		}
+
+		if (this._ValidProperties) {
+			this.postCreate();
+		}
+	}
+}
+
+dojo.lang.extend(dojo.iCalendar.Component, {
+
+	addProperty: function (prop) {
+		// summary
+		// push a new property onto a component.
+		this.properties.push(prop);
+		this[prop.name.toLowerCase()] = prop;
+	},
+
+	addComponent: function (prop) {
+		// summary
+		// add a component to this components list of children.
+		this.components.push(prop);
+	},
+
+	postCreate: function() {
+		for (var x=0; x<this._ValidProperties.length; x++) {
+			var evtProperty = this._ValidProperties[x];
+			var found = false;
+	
+			for (var y=0; y<this.properties.length; y++) {	
+				var prop = this.properties[y];
+				var propName = prop.name.toLowerCase();
+				if (dojo.lang.isArray(evtProperty)) {
+
+					var alreadySet = false;
+					for (var z=0; z<evtProperty.length; z++) {
+						var evtPropertyName = evtProperty[z].name.toLowerCase();
+						if((this[evtPropertyName])  && (evtPropertyName != propName )) {
+							alreadySet=true;
+						} 
+					}
+					if (!alreadySet) {
+						this[propName] = prop;
+					}
+				} else {
+					if (propName == evtProperty.name.toLowerCase()) {
+						found = true;
+						if (evtProperty.occurance == 1){
+							this[propName] = prop;
+						} else {
+							found = true;
+							if (!dojo.lang.isArray(this[propName])) {
+							 	this[propName] = [];
+							}
+							this[propName].push(prop);
+						}
+					}
+				}
+			}
+
+			if (evtProperty.required && !found) {	
+				dojo.debug("iCalendar - " + this.name + ": Required Property not found: " + evtProperty.name);
+			}
+		}
+
+		// parse any rrules		
+		if (dojo.lang.isArray(this.rrule)) {
+			for(var x=0; x<this.rrule.length; x++) {
+				var rule = this.rrule[x].value;
+
+				//add a place to cache dates we have checked for recurrance
+				this.rrule[x].cache = function() {};
+				
+				var temp = rule.split(";");
+				for (var y=0; y<temp.length; y++) {
+					var pair = temp[y].split("=");
+					var key = pair[0].toLowerCase();
+					var val = pair[1];
+
+					if ((key == "freq") || (key=="interval") || (key=="until")) {
+						this.rrule[x][key]= val;
+					} else {
+						var valArray = val.split(",");
+						this.rrule[x][key] = valArray; 
+					}
+				}	
+			}
+			this.recurring = true;
+		}
+
+	}, 
+
+	toString: function () {
+		// summary
+		// output a string representation of this component.
+		return "[iCalendar.Component; " + this.name + ", " + this.properties.length +
+			" properties, " + this.components.length + " components]";
+	}
+});
+
+dojo.iCalendar.Property = function (prop) {
+	// summary
+	// A single property of a component.
+
+	// unpack the values
+	this.name = prop.name;
+	this.group = prop.group;
+	this.params = prop.params;
+	this.value = prop.value;
+
+}
+
+dojo.lang.extend(dojo.iCalendar.Property, {
+	toString: function () {	
+		// summary
+		// output a string reprensentation of this component.
+		return "[iCalenday.Property; " + this.name + ": " + this.value + "]";
+	}
+});
+
+// This is just a little helper function for the Component Properties
+var _P = function (n, oc, req) {
+	return {name: n, required: (req) ? true : false,
+		occurance: (oc == '*' || !oc) ? -1 : oc}
+}
+
+/*
+ * VCALENDAR
+ */
+
+dojo.iCalendar.VCalendar = function (/* string */ calbody) {
+	// summary
+	// VCALENDAR Component
+
+	this.name = "VCALENDAR";
+	this.recurring = [];
+	this.nonRecurringEvents = function(){};
+	dojo.iCalendar.Component.call(this, calbody);
+}
+
+dojo.inherits(dojo.iCalendar.VCalendar, dojo.iCalendar.Component);
+
+dojo.lang.extend(dojo.iCalendar.VCalendar, {
+
+	addComponent: function (prop) {
+		// summary
+		// add component to the calenadar that makes it easy to pull them out again later.
+		this.components.push(prop);
+		if (prop.name.toLowerCase() == "vevent") {
+			if (prop.rrule) {
+				this.recurring.push(prop);
+			} else {
+				var startDate = prop.getDate();
+				var month = startDate.getMonth() + 1;
+				var dateString= month + "-" + startDate.getDate() + "-" + startDate.getFullYear();
+				if (!dojo.lang.isArray(this[dateString])) {
+					this.nonRecurringEvents[dateString] = [];
+				}
+				this.nonRecurringEvents[dateString].push(prop);
+			}
+		}
+	},
+
+	preComputeRecurringEvents: function(until) {
+		var calculatedEvents = function(){};
+
+		for(var x=0; x<this.recurring.length; x++) {
+			var dates = this.recurring[x].getDates(until);
+			for (var y=0; y<dates.length;y++) {
+				var month = dates[y].getMonth() + 1;
+				var dateStr = month + "-" + dates[y].getDate() + "-" + dates[y].getFullYear();
+				if (!dojo.lang.isArray(calculatedEvents[dateStr])) {
+					calculatedEvents[dateStr] = [];
+				}
+
+				if (!dojo.lang.inArray(calculatedEvents[dateStr], this.recurring[x])) { 
+					calculatedEvents[dateStr].push(this.recurring[x]);
+				} 
+			}
+		}
+		this.recurringEvents = calculatedEvents;
+	
+	},
+
+	getEvents: function(/* Date */ date) {
+		// summary
+		// Gets all events occuring on a particular date
+		var events = [];
+		var recur = [];
+		var nonRecur = [];
+		var month = date.getMonth() + 1;
+		var dateStr= month + "-" + date.getDate() + "-" + date.getFullYear();
+		if (dojo.lang.isArray(this.nonRecurringEvents[dateStr])) {
+			nonRecur= this.nonRecurringEvents[dateStr];
+			dojo.debug("Number of nonRecurring Events: " + nonRecur.length);
+		} 
+		
+
+		if (dojo.lang.isArray(this.recurringEvents[dateStr])) {
+			recur= this.recurringEvents[dateStr];
+		} 
+
+		events = recur.concat(nonRecur);
+
+		if (events.length > 0) {
+			return events;
+		} 
+
+		return null;			
+	}
+});
+
+/*
+ * STANDARD
+ */
+
+var StandardProperties = [
+	_P("dtstart", 1, true), _P("tzoffsetto", 1, true), _P("tzoffsetfrom", 1, true),
+	_P("comment"), _P("rdate"), _P("rrule"), _P("tzname")
+];
+
+
+dojo.iCalendar.Standard = function (/* string */ body) {
+	// summary
+	// STANDARD Component
+
+	this.name = "STANDARD";
+	this._ValidProperties = StandardProperties;
+	dojo.iCalendar.Component.call(this, body);
+}
+
+dojo.inherits(dojo.iCalendar.Standard, dojo.iCalendar.Component);
+
+/*
+ * DAYLIGHT
+ */
+
+var DaylightProperties = [
+	_P("dtstart", 1, true), _P("tzoffsetto", 1, true), _P("tzoffsetfrom", 1, true),
+	_P("comment"), _P("rdate"), _P("rrule"), _P("tzname")
+];
+
+dojo.iCalendar.Daylight = function (/* string */ body) {
+	// summary
+	// Daylight Component
+	this.name = "DAYLIGHT";
+	this._ValidProperties = DaylightProperties;
+	dojo.iCalendar.Component.call(this, body);
+}
+
+dojo.inherits(dojo.iCalendar.Daylight, dojo.iCalendar.Component);
+
+/*
+ * VEVENT
+ */
+
+var VEventProperties = [
+	// these can occur once only
+	_P("class", 1), _P("created", 1), _P("description", 1), _P("dtstart", 1),
+	_P("geo", 1), _P("last-mod", 1), _P("location", 1), _P("organizer", 1),
+	_P("priority", 1), _P("dtstamp", 1), _P("seq", 1), _P("status", 1),
+	_P("summary", 1), _P("transp", 1), _P("uid", 1), _P("url", 1), _P("recurid", 1),
+	// these two are exclusive
+	[_P("dtend", 1), _P("duration", 1)],
+	// these can occur many times over
+	_P("attach"), _P("attendee"), _P("categories"), _P("comment"), _P("contact"),
+	_P("exdate"), _P("exrule"), _P("rstatus"), _P("related"), _P("resources"),
+	_P("rdate"), _P("rrule")
+];
+
+dojo.iCalendar.VEvent = function (/* string */ body) {
+	// summary 
+	// VEVENT Component
+	this._ValidProperties = VEventProperties;
+	this.name = "VEVENT";
+	dojo.iCalendar.Component.call(this, body);
+	this.recurring = false;
+	this.startDate = dojo.date.fromIso8601(this.dtstart.value);
+}
+
+dojo.inherits(dojo.iCalendar.VEvent, dojo.iCalendar.Component);
+
+dojo.lang.extend(dojo.iCalendar.VEvent, {
+		getDates: function(until) {
+			var dtstart = this.getDate();
+
+			var recurranceSet = [];
+			var weekdays=["su","mo","tu","we","th","fr","sa"];
+			var order = { 
+				"daily": 1, "weekly": 2, "monthly": 3, "yearly": 4,
+				"byday": 1, "bymonthday": 1, "byweekno": 2, "bymonth": 3, "byyearday": 4};
+
+			// expand rrules into the recurrance 
+			for (var x=0; x<this.rrule.length; x++) {
+				var rrule = this.rrule[x];
+				var freq = rrule.freq.toLowerCase();
+				var interval = 1;
+
+				if (rrule.interval > interval) {
+					interval = rrule.interval;
+				}
+
+				var set = [];
+				var freqInt = order[freq];
+
+				if (rrule.until) {
+					var tmpUntil = dojo.date.fromIso8601(rrule.until);
+				} else {
+					var tmpUntil = until
+				}
+
+				if (tmpUntil > until) {
+					tmpUntil = until
+				}
+
+
+				if (dtstart<tmpUntil) {
+
+					var expandingRules = function(){};
+					var cullingRules = function(){};
+					expandingRules.length=0;
+					cullingRules.length =0;
+
+					switch(freq) {
+						case "yearly":
+							var nextDate = new Date(dtstart);
+							set.push(nextDate);
+							while(nextDate < tmpUntil) {
+								nextDate.setYear(nextDate.getFullYear()+interval);
+								tmpDate = new Date(nextDate);
+								if(tmpDate < tmpUntil) {
+									set.push(tmpDate);
+								}
+							}
+							break;
+						case "monthly":
+							nextDate = new Date(dtstart);
+							set.push(nextDate);
+							while(nextDate < tmpUntil) {
+								nextDate.setMonth(nextDate.getMonth()+interval);
+								var tmpDate = new Date(nextDate);
+								if (tmpDate < tmpUntil) {
+									set.push(tmpDate);
+								}
+							}
+							break;
+						case "weekly":
+							nextDate = new Date(dtstart);
+							set.push(nextDate);
+							while(nextDate < tmpUntil) {
+								nextDate.setDate(nextDate.getDate()+(7*interval));
+								var tmpDate = new Date(nextDate);
+								if (tmpDate < tmpUntil) {
+									set.push(tmpDate);
+								}
+							}
+							break;	
+						case "daily":
+							nextDate = new Date(dtstart);
+							set.push(nextDate);
+							while(nextDate < tmpUntil) {
+								nextDate.setDate(nextDate.getDate()+interval);
+								var tmpDate = new Date(nextDate);
+								if (tmpDate < tmpUntil) {
+									set.push(tmpDate);
+								}
+							}
+							break;
+	
+					}
+
+					if ((rrule["bymonth"]) && (order["bymonth"]<freqInt))	{
+						for (var z=0; z<rrule["bymonth"].length; z++) {
+							if (z==0) {
+								for (var zz=0; zz < set.length; zz++) {
+									set[zz].setMonth(rrule["bymonth"][z]-1);
+								}
+							} else {
+								var subset=[];
+								for (var zz=0; zz < set.length; zz++) {
+									var newDate = new Date(set[zz]);
+									newDate.setMonth(rrule[z]);
+									subset.push(newDate);
+								}
+								tmp = set.concat(subset);
+								set = tmp;
+							}
+						}
+					}
+
+					
+					// while the spec doesn't prohibit it, it makes no sense to have a bymonth and a byweekno at the same time
+					// and if i'm wrong then i don't know how to apply that rule.  This is also documented elsewhere on the web
+					if (rrule["byweekno"] && !rrule["bymonth"]) {	
+						dojo.debug("TODO: no support for byweekno yet");
+					}
+
+
+					// while the spec doesn't prohibit it, it makes no sense to have a bymonth and a byweekno at the same time
+					// and if i'm wrong then i don't know how to apply that rule.  This is also documented elsewhere on the web
+					if (rrule["byyearday"] && !rrule["bymonth"] && !rrule["byweekno"] ) {	
+						if (rrule["byyearday"].length > 1) {
+							var regex = "([+-]?)([0-9]{1,3})";
+							for (var z=1; x<rrule["byyearday"].length; z++) {
+								var regexResult = rrule["byyearday"][z].match(regex);
+								if (z==1) {
+									for (var zz=0; zz < set.length; zz++) {
+										if (regexResult[1] == "-") {
+											dojo.date.setDayOfYear(set[zz],366-regexResult[2]);
+										} else {
+											dojo.date.setDayOfYear(set[zz],regexResult[2]);
+										}
+									}
+								}	else {
+									var subset=[];
+									for (var zz=0; zz < set.length; zz++) {
+										var newDate = new Date(set[zz]);
+										if (regexResult[1] == "-") {
+											dojo.date.setDayOfYear(newDate,366-regexResult[2]);
+										} else {
+											dojo.date.setDayOfYear(newDate,regexResult[2]);
+										}
+										subset.push(newDate);
+									}
+									tmp = set.concat(subset);
+									set = tmp;
+								}
+							}
+						}
+					}
+
+					if (rrule["bymonthday"]  && (order["bymonthday"]<freqInt)) {	
+						if (rrule["bymonthday"].length > 0) {
+							var regex = "([+-]?)([0-9]{1,3})";
+							for (var z=0; z<rrule["bymonthday"].length; z++) {
+								var regexResult = rrule["bymonthday"][z].match(regex);
+								if (z==0) {
+									for (var zz=0; zz < set.length; zz++) {
+										if (regexResult[1] == "-") {
+											if (regexResult[2] < dojo.date.getDaysInMonth(set[zz])) {
+												set[zz].setDate(dojo.date.getDaysInMonth(set[zz]) - regexResult[2]);
+											}
+										} else {
+											if (regexResult[2] < dojo.date.getDaysInMonth(set[zz])) {
+												set[zz].setDate(regexResult[2]);
+											}
+										}
+									}
+								}	else {
+									var subset=[];
+									for (var zz=0; zz < set.length; zz++) {
+										var newDate = new Date(set[zz]);
+										if (regexResult[1] == "-") {
+											if (regexResult[2] < dojo.date.getDaysInMonth(set[zz])) {
+												newDate.setDate(dojo.date.getDaysInMonth(set[zz]) - regexResult[2]);
+											}
+										} else {
+											if (regexResult[2] < dojo.date.getDaysInMonth(set[zz])) {
+												newDate.setDate(regexResult[2]);
+											}
+										}
+										subset.push(newDate);
+									}
+									tmp = set.concat(subset);
+									set = tmp;
+								}
+							}
+						}
+					}
+
+					if (rrule["byday"]  && (order["byday"]<freqInt)) {	
+						if (rrule["bymonth"]) {
+							if (rrule["byday"].length > 0) {
+								var regex = "([+-]?)([0-9]{0,1}?)([A-Za-z]{1,2})";
+								for (var z=0; z<rrule["byday"].length; z++) {
+									var regexResult = rrule["byday"][z].match(regex);
+									var occurance = regexResult[2];
+									var day = regexResult[3].toLowerCase();
+
+
+									if (z==0) {
+										for (var zz=0; zz < set.length; zz++) {
+											if (regexResult[1] == "-") {
+												//find the nth to last occurance of date 
+												var numDaysFound = 0;
+												var lastDayOfMonth = dojo.date.getDaysInMonth(set[zz]);
+												var daysToSubtract = 1;
+												set[zz].setDate(lastDayOfMonth); 
+												if (weekdays[set[zz].getDay()] == day) {
+													numDaysFound++;
+													daysToSubtract=7;
+												}
+												daysToSubtract = 1;
+												while (numDaysFound < occurance) {
+													set[zz].setDate(set[zz].getDate()-daysToSubtract);	
+													if (weekdays[set[zz].getDay()] == day) {
+														numDaysFound++;
+														daysToSubtract=7;	
+													}
+												}
+											} else {
+												if (occurance) {
+													var numDaysFound=0;
+													set[zz].setDate(1);
+													var daysToAdd=1;
+
+													if(weekdays[set[zz].getDay()] == day) {
+														numDaysFound++;
+														daysToAdd=7;
+													}
+
+													while(numDaysFound < occurance) {
+														set[zz].setDate(set[zz].getDate()+daysToAdd);
+														if(weekdays[set[zz].getDay()] == day) {
+															numDaysFound++;
+															daysToAdd=7;
+														}
+													}
+												} else {
+													//we're gonna expand here to add a date for each of the specified days for each month
+													var numDaysFound=0;
+													var subset = [];
+
+													lastDayOfMonth = new Date(set[zz]);
+													var daysInMonth = dojo.date.getDaysInMonth(set[zz]);
+													lastDayOfMonth.setDate(daysInMonth);
+
+													set[zz].setDate(1);
+												
+													if (weekdays[set[zz].getDay()] == day) {
+														numDaysFound++;
+													}
+													var tmpDate = new Date(set[zz]);
+													daysToAdd = 1;
+													while(tmpDate.getDate() < lastDayOfMonth) {
+														if (weekdays[tmpDate.getDay()] == day) {
+															numDaysFound++;
+															if (numDaysFound==1) {
+																set[zz] = tmpDate;
+															} else {
+																subset.push(tmpDate);
+																tmpDate = new Date(tmpDate);
+																daysToAdd=7;	
+																tmpDate.setDate(tmpDate.getDate() + daysToAdd);
+															}
+														} else {
+															tmpDate.setDate(tmpDate.getDate() + daysToAdd);
+														}
+													}
+													var t = set.concat(subset);
+													set = t; 
+												}
+											}
+										}
+									}	else {
+										var subset=[];
+										for (var zz=0; zz < set.length; zz++) {
+											var newDate = new Date(set[zz]);
+											if (regexResult[1] == "-") {
+												if (regexResult[2] < dojo.date.getDaysInMonth(set[zz])) {
+													newDate.setDate(dojo.date.getDaysInMonth(set[zz]) - regexResult[2]);
+												}
+											} else {
+												if (regexResult[2] < dojo.date.getDaysInMonth(set[zz])) {
+													newDate.setDate(regexResult[2]);
+												}
+											}
+											subset.push(newDate);
+										}
+										tmp = set.concat(subset);
+										set = tmp;
+									}
+								}
+							}
+						} else {
+							dojo.debug("TODO: byday within a yearly rule without a bymonth");
+						}
+					}
+
+					dojo.debug("TODO: Process BYrules for units larger than frequency");
+			
+					//add this set of events to the complete recurranceSet	
+					var tmp = recurranceSet.concat(set);
+					recurranceSet = tmp;
+				}
+			}
+
+			// TODO: add rdates to the recurrance set here
+
+			// TODO: subtract exdates from the recurrance set here
+
+			//TODO:  subtract dates generated by exrules from recurranceSet here
+
+			recurranceSet.push(dtstart);
+			return recurranceSet;
+		},
+
+		getDate: function() {
+			return dojo.date.fromIso8601(this.dtstart.value);
+		}
+});
+
+/*
+ * VTIMEZONE
+ */
+
+var VTimeZoneProperties = [
+	_P("tzid", 1, true), _P("last-mod", 1), _P("tzurl", 1)
+
+	// one of 'standardc' or 'daylightc' must occur
+	// and each may occur more than once.
+];
+
+dojo.iCalendar.VTimeZone = function (/* string */ body) {
+	// summary
+	// VTIMEZONE Component
+	this.name = "VTIMEZONE";
+	this._ValidProperties = VTimeZoneProperties;
+	dojo.iCalendar.Component.call(this, body);
+}
+
+dojo.inherits(dojo.iCalendar.VTimeZone, dojo.iCalendar.Component);
+
+/*
+ * VTODO
+ */
+
+var VTodoProperties = [
+	// these can occur once only
+	_P("class", 1), _P("completed", 1), _P("created", 1), _P("description", 1),
+	_P("dtstart", 1), _P("geo", 1), _P("last-mod", 1), _P("location", 1),
+	_P("organizer", 1), _P("percent", 1), _P("priority", 1), _P("dtstamp", 1),
+	_P("seq", 1), _P("status", 1), _P("summary", 1), _P("uid", 1), _P("url", 1),
+	_P("recurid", 1),
+	// these two are exclusive
+	[_P("due", 1), _P("duration", 1)],
+	// these can occur many times over
+	_P("attach"), _P("attendee"), _P("categories"), _P("comment"), _P("contact"),
+	_P("exdate"), _P("exrule"), _P("rstatus"), _P("related"), _P("resources"),
+	_P("rdate"), _P("rrule")
+];
+
+dojo.iCalendar.VTodo= function (/* string */ body) {
+	// summary
+	// VTODO Componenet
+	this.name = "VTODO";
+	this._ValidProperties = VTodoProperties;
+	dojo.iCalendar.Component.call(this, body);
+}
+
+dojo.inherits(dojo.iCalendar.VTodo, dojo.iCalendar.Component);
+
+/*
+ * VJOURNAL
+ */
+
+var VJournalProperties = [
+	// these can occur once only
+	_P("class", 1), _P("created", 1), _P("description", 1), _P("dtstart", 1),
+	_P("last-mod", 1), _P("organizer", 1), _P("dtstamp", 1), _P("seq", 1),
+	_P("status", 1), _P("summary", 1), _P("uid", 1), _P("url", 1), _P("recurid", 1),
+	// these can occur many times over
+	_P("attach"), _P("attendee"), _P("categories"), _P("comment"), _P("contact"),
+	_P("exdate"), _P("exrule"), _P("related"), _P("rstatus"), _P("rdate"), _P("rrule")
+];
+
+dojo.iCalendar.VJournal= function (/* string */ body) {
+	// summary
+	// VJOURNAL Component
+	this.name = "VJOURNAL";
+	this._ValidProperties = VJournalProperties;
+	dojo.iCalendar.Component.call(this, body);
+}
+
+dojo.inherits(dojo.iCalendar.VJournal, dojo.iCalendar.Component);
+
+/*
+ * VFREEBUSY
+ */
+
+var VFreeBusyProperties = [
+	// these can occur once only
+	_P("contact"), _P("dtstart", 1), _P("dtend"), _P("duration"),
+	_P("organizer", 1), _P("dtstamp", 1), _P("uid", 1), _P("url", 1),
+	// these can occur many times over
+	_P("attendee"), _P("comment"), _P("freebusy"), _P("rstatus")
+];
+
+dojo.iCalendar.VFreeBusy= function (/* string */ body) {
+	// summary
+	// VFREEBUSY Component
+	this.name = "VFREEBUSY";
+	this._ValidProperties = VFreeBusyProperties;
+	dojo.iCalendar.Component.call(this, body);
+}
+
+dojo.inherits(dojo.iCalendar.VFreeBusy, dojo.iCalendar.Component);
+
+/*
+ * VALARM
+ */
+
+var VAlarmProperties = [
+	[_P("action", 1, true), _P("trigger", 1, true), [_P("duration", 1), _P("repeat", 1)],
+	_P("attach", 1)],
+
+	[_P("action", 1, true), _P("description", 1, true), _P("trigger", 1, true),
+	[_P("duration", 1), _P("repeat", 1)]],
+
+	[_P("action", 1, true), _P("description", 1, true), _P("trigger", 1, true),
+	_P("summary", 1, true), _P("attendee", "*", true),
+	[_P("duration", 1), _P("repeat", 1)],
+	_P("attach", 1)],
+
+	[_P("action", 1, true), _P("attach", 1, true), _P("trigger", 1, true),
+	[_P("duration", 1), _P("repeat", 1)],
+	_P("description", 1)],
+];
+
+dojo.iCalendar.VAlarm= function (/* string */ body) {
+	// summary
+	// VALARM Component
+	this.name = "VALARM";
+	this._ValidProperties = VAlarmProperties;
+	dojo.iCalendar.Component.call(this, body);
+}
+
+dojo.inherits(dojo.iCalendar.VAlarm, dojo.iCalendar.Component);
+


Property changes on: trunk/PhotoOp/root/static/js/src/iCalendar.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/io/BrowserIO.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/io/BrowserIO.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/io/BrowserIO.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,557 @@
+/*
+	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.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 hasFile = false;
+	var inputs = node.getElementsByTagName("input");
+	dojo.lang.forEach(inputs, function(input){
+		if(hasFile){ return; }
+		if(input.getAttribute("type")=="file"){
+			hasFile = true;
+		}
+	});
+	return hasFile;
+}
+
+dojo.io.formHasFile = function(formNode){
+	return dojo.io.checkChildrenForFile(formNode);
+}
+
+dojo.io.updateNode = function(node, urlOrArgs){
+	node = dojo.byId(node);
+	var args = urlOrArgs;
+	if(dojo.lang.isString(urlOrArgs)){
+		args = { url: urlOrArgs };
+	}
+	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"]);
+}
+
+// TODO: Move to htmlUtils
+dojo.io.encodeForm = function(formNode, encoding, formFilter){
+	if((!formNode)||(!formNode.tagName)||(!formNode.tagName.toLowerCase() == "form")){
+		dojo.raise("Attempted to encode a non-form element.");
+	}
+	if(!formFilter) { formFilter = dojo.io.formFilter; }
+	var enc = /utf/i.test(encoding||"") ? encodeURIComponent : dojo.string.encodeAscii;
+	var values = [];
+
+	for(var i = 0; i < formNode.elements.length; i++){
+		var elm = formNode.elements[i];
+		if(!elm || elm.tagName.toLowerCase() == "fieldset" || !formFilter(elm)) { continue; }
+		var name = enc(elm.name);
+		var type = elm.type.toLowerCase();
+
+		if(type == "select-multiple"){
+			for(var j = 0; j < elm.options.length; j++){
+				if(elm.options[j].selected) {
+					values.push(name + "=" + enc(elm.options[j].value));
+				}
+			}
+		}else if(dojo.lang.inArray(type, ["radio", "checkbox"])){
+			if(elm.checked){
+				values.push(name + "=" + enc(elm.value));
+			}
+		}else{
+			values.push(name + "=" + enc(elm.value));
+		}
+	}
+
+	// now collect input type="image", which doesn't show up in the elements array
+	var inputs = formNode.getElementsByTagName("input");
+	for(var i = 0; i < inputs.length; i++) {
+		var input = inputs[i];
+		if(input.type.toLowerCase() == "image" && input.form == formNode
+			&& formFilter(input)) {
+			var name = enc(input.name);
+			values.push(name + "=" + enc(input.value));
+			values.push(name + ".x=0");
+			values.push(name + ".y=0");
+		}
+	}
+	return values.join("&") + "&";
+}
+
+dojo.io.FormBind = function(args) {
+	this.bindArgs = {};
+
+	if(args && args.formNode) {
+		this.init(args);
+	} else if(args) {
+		this.init({formNode: args});
+	}
+}
+dojo.lang.extend(dojo.io.FormBind, {
+	form: null,
+
+	bindArgs: null,
+
+	clickedButton: null,
+
+	init: function(args) {
+		var form = dojo.byId(args.formNode);
+
+		if(!form || !form.tagName || form.tagName.toLowerCase() != "form") {
+			throw new Error("FormBind: Couldn't apply, invalid form");
+		} else if(this.form == form) {
+			return;
+		} else if(this.form) {
+			throw new Error("FormBind: Already applied to a form");
+		}
+
+		dojo.lang.mixin(this.bindArgs, args);
+		this.form = form;
+
+		this.connect(form, "onsubmit", "submit");
+
+		for(var i = 0; i < form.elements.length; i++) {
+			var node = form.elements[i];
+			if(node && node.type && dojo.lang.inArray(node.type.toLowerCase(), ["submit", "button"])) {
+				this.connect(node, "onclick", "click");
+			}
+		}
+
+		var inputs = form.getElementsByTagName("input");
+		for(var i = 0; i < inputs.length; i++) {
+			var input = inputs[i];
+			if(input.type.toLowerCase() == "image" && input.form == form) {
+				this.connect(input, "onclick", "click");
+			}
+		}
+	},
+
+	onSubmit: function(form) {
+		return true;
+	},
+
+	submit: function(e) {
+		e.preventDefault();
+		if(this.onSubmit(this.form)) {
+			dojo.io.bind(dojo.lang.mixin(this.bindArgs, {
+				formFilter: dojo.lang.hitch(this, "formFilter")
+			}));
+		}
+	},
+
+	click: function(e) {
+		var node = e.currentTarget;
+		if(node.disabled) { return; }
+		this.clickedButton = node;
+	},
+
+	formFilter: function(node) {
+		var type = (node.type||"").toLowerCase();
+		var accept = false;
+		if(node.disabled || !node.name) {
+			accept = false;
+		} else if(dojo.lang.inArray(type, ["submit", "button", "image"])) {
+			if(!this.clickedButton) { this.clickedButton = node; }
+			accept = node == this.clickedButton;
+		} else {
+			accept = !dojo.lang.inArray(type, ["file", "submit", "reset", "button"]);
+		}
+		return accept;
+	},
+
+	// in case you don't have dojo.event.* pulled in
+	connect: function(srcObj, srcFcn, targetFcn) {
+		if(dojo.evalObjPath("dojo.event.connect")) {
+			dojo.event.connect(srcObj, srcFcn, this, targetFcn);
+		} else {
+			var fcn = dojo.lang.hitch(this, targetFcn);
+			srcObj[srcFcn] = function(e) {
+				if(!e) { e = window.event; }
+				if(!e.currentTarget) { e.currentTarget = e.srcElement; }
+				if(!e.preventDefault) { e.preventDefault = function() { window.event.returnValue = false; } }
+				fcn(e);
+			}
+		}
+	}
+});
+
+dojo.io.XMLHTTPTransport = new function(){
+	var _this = this;
+
+	var _cache = {}; // FIXME: make this public? do we even need to?
+	this.useCache = false; // if this is true, we'll cache unless kwArgs.useCache = false
+	this.preventCache = false; // if this is true, we'll always force GET requests to cache
+
+	// FIXME: Should this even be a function? or do we just hard code it in the next 2 functions?
+	function getCacheKey(url, query, method) {
+		return url + "|" + query + "|" + method.toLowerCase();
+	}
+
+	function addToCache(url, query, method, http) {
+		_cache[getCacheKey(url, query, method)] = http;
+	}
+
+	function getFromCache(url, query, method) {
+		return _cache[getCacheKey(url, query, method)];
+	}
+
+	this.clearCache = function() {
+		_cache = {};
+	}
+
+	// moved successful load stuff here
+	function doLoad(kwArgs, http, url, query, useCache) {
+		if(	((http.status>=200)&&(http.status<300))|| 	// allow any 2XX response code
+			(http.status==304)|| 						// get it out of the cache
+			(location.protocol=="file:" && (http.status==0 || http.status==undefined))||
+			(location.protocol=="chrome:" && (http.status==0 || http.status==undefined))
+		){
+			var ret;
+			if(kwArgs.method.toLowerCase() == "head"){
+				var headers = http.getAllResponseHeaders();
+				ret = {};
+				ret.toString = function(){ return headers; }
+				var values = headers.split(/[\r\n]+/g);
+				for(var i = 0; i < values.length; i++) {
+					var pair = values[i].match(/^([^:]+)\s*:\s*(.+)$/i);
+					if(pair) {
+						ret[pair[1]] = pair[2];
+					}
+				}
+			}else if(kwArgs.mimetype == "text/javascript"){
+				try{
+					ret = dj_eval(http.responseText);
+				}catch(e){
+					dojo.debug(e);
+					dojo.debug(http.responseText);
+					ret = null;
+				}
+			}else if(kwArgs.mimetype == "text/json"){
+				try{
+					ret = dj_eval("("+http.responseText+")");
+				}catch(e){
+					dojo.debug(e);
+					dojo.debug(http.responseText);
+					ret = false;
+				}
+			}else if((kwArgs.mimetype == "application/xml")||
+						(kwArgs.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(useCache){ // only cache successful responses
+				addToCache(url, query, kwArgs.method, http);
+			}
+			kwArgs[(typeof kwArgs.load == "function") ? "load" : "handle"]("load", ret, http, kwArgs);
+		}else{
+			var errObj = new dojo.io.Error("XMLHttpTransport Error: "+http.status+" "+http.statusText);
+			kwArgs[(typeof kwArgs.error == "function") ? "error" : "handle"]("error", errObj, http, kwArgs);
+		}
+	}
+
+	// set headers (note: Content-Type will get overriden if kwArgs.contentType is set)
+	function setHeaders(http, kwArgs){
+		if(kwArgs["headers"]) {
+			for(var header in kwArgs["headers"]) {
+				if(header.toLowerCase() == "content-type" && !kwArgs["contentType"]) {
+					kwArgs["contentType"] = kwArgs["headers"][header];
+				} else {
+					http.setRequestHeader(header, kwArgs["headers"][header]);
+				}
+			}
+		}
+	}
+
+	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){
+				// remove it so we can clean refs
+				this.inFlight.splice(x, 1);
+				doLoad(tif.req, tif.http, tif.url, tif.query, tif.useCache);
+			}else if (tif.startTime){
+				//See if this is a timeout case.
+				if(!now){
+					now = (new Date()).getTime();
+				}
+				if(tif.startTime + (tif.req.timeoutSeconds * 1000) < now){
+					//Stop the request.
+					if(typeof tif.http.abort == "function"){
+						tif.http.abort();
+					}
+
+					// remove it so we can clean refs
+					this.inFlight.splice(x, 1);
+					tif.req[(typeof tif.req.timeout == "function") ? "timeout" : "handle"]("timeout", null, tif.http, tif.req);
+				}
+			}
+		}
+
+		if(this.inFlight.length == 0){
+			clearInterval(this.inFlightTimer);
+			this.inFlightTimer = null;
+		}
+	}
+
+	var hasXmlHttp = dojo.hostenv.getXmlhttpObject() ? true : false;
+	this.canHandle = function(kwArgs){
+		// canHandle just tells dojo.io.bind() if this is a good transport to
+		// use for the particular type of request.
+
+		// FIXME: we need to determine when form values need to be
+		// multi-part mime encoded and avoid using this transport for those
+		// requests.
+		return hasXmlHttp
+			&& dojo.lang.inArray((kwArgs["mimetype"].toLowerCase()||""), ["text/plain", "text/html", "application/xml", "text/xml", "text/javascript", "text/json"])
+			&& !( kwArgs["formNode"] && dojo.io.formHasFile(kwArgs["formNode"]) );
+	}
+
+	this.multipartBoundary = "45309FFF-BD65-4d50-99C9-36986896A96F";	// unique guid as a boundary value for multipart posts
+
+	this.bind = function(kwArgs){
+		if(!kwArgs["url"]){
+			// are we performing a history action?
+			if( !kwArgs["formNode"]
+				&& (kwArgs["backButton"] || kwArgs["back"] || kwArgs["changeUrl"] || kwArgs["watchForURL"])
+				&& (!djConfig.preventBackButtonFix)) {
+        dojo.deprecated("Using dojo.io.XMLHTTPTransport.bind() to add to browser history without doing an IO request",
+        				"Use dojo.undo.browser.addToHistory() instead.", "0.4");
+				dojo.undo.browser.addToHistory(kwArgs);
+				return true;
+			}
+		}
+
+		// build this first for cache purposes
+		var url = kwArgs.url;
+		var query = "";
+		if(kwArgs["formNode"]){
+			var ta = kwArgs.formNode.getAttribute("action");
+			if((ta)&&(!kwArgs["url"])){ url = ta; }
+			var tp = kwArgs.formNode.getAttribute("method");
+			if((tp)&&(!kwArgs["method"])){ kwArgs.method = tp; }
+			query += dojo.io.encodeForm(kwArgs.formNode, kwArgs.encoding, kwArgs["formFilter"]);
+		}
+
+		if(url.indexOf("#") > -1) {
+			dojo.debug("Warning: dojo.io.bind: stripping hash values from url:", url);
+			url = url.split("#")[0];
+		}
+
+		if(kwArgs["file"]){
+			// force post for file transfer
+			kwArgs.method = "post";
+		}
+
+		if(!kwArgs["method"]){
+			kwArgs.method = "get";
+		}
+
+		// guess the multipart value		
+		if(kwArgs.method.toLowerCase() == "get"){
+			// GET cannot use multipart
+			kwArgs.multipart = false;
+		}else{
+			if(kwArgs["file"]){
+				// enforce multipart when sending files
+				kwArgs.multipart = true;
+			}else if(!kwArgs["multipart"]){
+				// default 
+				kwArgs.multipart = false;
+			}
+		}
+
+		if(kwArgs["backButton"] || kwArgs["back"] || kwArgs["changeUrl"]){
+			dojo.undo.browser.addToHistory(kwArgs);
+		}
+
+		var content = kwArgs["content"] || {};
+
+		if(kwArgs.sendTransport) {
+			content["dojo.transport"] = "xmlhttp";
+		}
+
+		do { // break-block
+			if(kwArgs.postContent){
+				query = kwArgs.postContent;
+				break;
+			}
+
+			if(content) {
+				query += dojo.io.argsFromMap(content, kwArgs.encoding);
+			}
+			
+			if(kwArgs.method.toLowerCase() == "get" || !kwArgs.multipart){
+				break;
+			}
+
+			var	t = [];
+			if(query.length){
+				var q = query.split("&");
+				for(var i = 0; i < q.length; ++i){
+					if(q[i].length){
+						var p = q[i].split("=");
+						t.push(	"--" + this.multipartBoundary,
+								"Content-Disposition: form-data; name=\"" + p[0] + "\"", 
+								"",
+								p[1]);
+					}
+				}
+			}
+
+			if(kwArgs.file){
+				if(dojo.lang.isArray(kwArgs.file)){
+					for(var i = 0; i < kwArgs.file.length; ++i){
+						var o = kwArgs.file[i];
+						t.push(	"--" + this.multipartBoundary,
+								"Content-Disposition: form-data; name=\"" + o.name + "\"; filename=\"" + ("fileName" in o ? o.fileName : o.name) + "\"",
+								"Content-Type: " + ("contentType" in o ? o.contentType : "application/octet-stream"),
+								"",
+								o.content);
+					}
+				}else{
+					var o = kwArgs.file;
+					t.push(	"--" + this.multipartBoundary,
+							"Content-Disposition: form-data; name=\"" + o.name + "\"; filename=\"" + ("fileName" in o ? o.fileName : o.name) + "\"",
+							"Content-Type: " + ("contentType" in o ? o.contentType : "application/octet-stream"),
+							"",
+							o.content);
+				}
+			}
+
+			if(t.length){
+				t.push("--"+this.multipartBoundary+"--", "");
+				query = t.join("\r\n");
+			}
+		}while(false);
+
+		// kwArgs.Connection = "close";
+
+		var async = kwArgs["sync"] ? false : true;
+
+		var preventCache = kwArgs["preventCache"] ||
+			(this.preventCache == true && kwArgs["preventCache"] != false);
+		var useCache = kwArgs["useCache"] == true ||
+			(this.useCache == true && kwArgs["useCache"] != false );
+
+		// preventCache is browser-level (add query string junk), useCache
+		// is for the local cache. If we say preventCache, then don't attempt
+		// to look in the cache, but if useCache is true, we still want to cache
+		// the response
+		if(!preventCache && useCache){
+			var cachedHttp = getFromCache(url, query, kwArgs.method);
+			if(cachedHttp){
+				doLoad(kwArgs, cachedHttp, url, query, false);
+				return;
+			}
+		}
+
+		// much of this is from getText, but reproduced here because we need
+		// more flexibility
+		var http = dojo.hostenv.getXmlhttpObject(kwArgs);	
+		var received = false;
+
+		// build a handler function that calls back to the handler obj
+		if(async){
+			var startTime = 
+			// FIXME: setting up this callback handler leaks on IE!!!
+			this.inFlight.push({
+				"req":		kwArgs,
+				"http":		http,
+				"url":	 	url,
+				"query":	query,
+				"useCache":	useCache,
+				"startTime": kwArgs.timeoutSeconds ? (new Date()).getTime() : 0
+			});
+			this.startWatchingInFlight();
+		}
+
+		if(kwArgs.method.toLowerCase() == "post"){
+			// FIXME: need to hack in more flexible Content-Type setting here!
+			http.open("POST", url, async);
+			setHeaders(http, kwArgs);
+			http.setRequestHeader("Content-Type", kwArgs.multipart ? ("multipart/form-data; boundary=" + this.multipartBoundary) : 
+				(kwArgs.contentType || "application/x-www-form-urlencoded"));
+			try{
+				http.send(query);
+			}catch(e){
+				if(typeof http.abort == "function"){
+					http.abort();
+				}
+				doLoad(kwArgs, {status: 404}, url, query, useCache);
+			}
+		}else{
+			var tmpUrl = url;
+			if(query != "") {
+				tmpUrl += (tmpUrl.indexOf("?") > -1 ? "&" : "?") + query;
+			}
+			if(preventCache) {
+				tmpUrl += (dojo.string.endsWithAny(tmpUrl, "?", "&")
+					? "" : (tmpUrl.indexOf("?") > -1 ? "&" : "?")) + "dojo.preventCache=" + new Date().valueOf();
+			}
+			http.open(kwArgs.method.toUpperCase(), tmpUrl, async);
+			setHeaders(http, kwArgs);
+			try {
+				http.send(null);
+			}catch(e)	{
+				if(typeof http.abort == "function"){
+					http.abort();
+				}
+				doLoad(kwArgs, {status: 404}, url, query, useCache);
+			}
+		}
+
+		if( !async ) {
+			doLoad(kwArgs, http, url, query, useCache);
+		}
+
+		kwArgs.abort = function(){
+			return http.abort();
+		}
+
+		return;
+	}
+	dojo.io.transports.addTransport("XMLHTTPTransport");
+}


Property changes on: trunk/PhotoOp/root/static/js/src/io/BrowserIO.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/io/IframeIO.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/io/IframeIO.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/io/IframeIO.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,253 @@
+/*
+	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.io.IframeIO");
+dojo.require("dojo.io.BrowserIO");
+dojo.require("dojo.uri.*");
+
+// FIXME: is it possible to use the Google htmlfile hack to prevent the
+// background click with this transport?
+
+dojo.io.createIFrame = function(fname, onloadstr){
+	if(window[fname]){ return window[fname]; }
+	if(window.frames[fname]){ return window.frames[fname]; }
+	var r = dojo.render.html;
+	var cframe = null;
+	var turi = dojo.uri.dojoUri("iframe_history.html?noInit=true");
+	var ifrstr = ((r.ie)&&(dojo.render.os.win)) ? "<iframe name='"+fname+"' src='"+turi+"' onload='"+onloadstr+"'>" : "iframe";
+	cframe = document.createElement(ifrstr);
+	with(cframe){
+		name = fname;
+		setAttribute("name", fname);
+		id = fname;
+	}
+	(document.body||document.getElementsByTagName("body")[0]).appendChild(cframe);
+	window[fname] = cframe;
+	with(cframe.style){
+		position = "absolute";
+		left = top = "0px";
+		height = width = "1px";
+		visibility = "hidden";
+		/*
+		if(djConfig.isDebug){
+			position = "relative";
+			height = "300px";
+			width = "600px";
+			visibility = "visible";
+		}
+		*/
+	}
+
+	if(!r.ie){
+		dojo.io.setIFrameSrc(cframe, turi, true);
+		cframe.onload = new Function(onloadstr);
+	}
+	return cframe;
+}
+
+// thanks burstlib!
+dojo.io.iframeContentWindow = function(iframe_el) {
+	var win = iframe_el.contentWindow || // IE
+		dojo.io.iframeContentDocument(iframe_el).defaultView || // Moz, opera
+		// Moz. TODO: is this available when defaultView isn't?
+		dojo.io.iframeContentDocument(iframe_el).__parent__ || 
+		(iframe_el.name && document.frames[iframe_el.name]) || null;
+	return win;
+}
+
+dojo.io.iframeContentDocument = function(iframe_el){
+	var doc = iframe_el.contentDocument || // W3
+		(
+			(iframe_el.contentWindow)&&(iframe_el.contentWindow.document)
+		) ||  // IE
+		(
+			(iframe_el.name)&&(document.frames[iframe_el.name])&&
+			(document.frames[iframe_el.name].document)
+		) || null;
+	return doc;
+}
+
+dojo.io.IframeTransport = new function(){
+	var _this = this;
+	this.currentRequest = null;
+	this.requestQueue = [];
+	this.iframeName = "dojoIoIframe";
+
+	this.fireNextRequest = function(){
+		if((this.currentRequest)||(this.requestQueue.length == 0)){ return; }
+		// dojo.debug("fireNextRequest");
+		var cr = this.currentRequest = this.requestQueue.shift();
+		cr._contentToClean = [];
+		var fn = cr["formNode"];
+		var content = cr["content"] || {};
+		if(cr.sendTransport) {
+			content["dojo.transport"] = "iframe";
+		}
+		if(fn){
+			if(content){
+				// if we have things in content, we need to add them to the form
+				// before submission
+				for(var x in content){
+					if(!fn[x]){
+						var tn;
+						if(dojo.render.html.ie){
+							tn = document.createElement("<input type='hidden' name='"+x+"' value='"+content[x]+"'>");
+							fn.appendChild(tn);
+						}else{
+							tn = document.createElement("input");
+							fn.appendChild(tn);
+							tn.type = "hidden";
+							tn.name = x;
+							tn.value = content[x];
+						}
+						cr._contentToClean.push(x);
+					}else{
+						fn[x].value = content[x];
+					}
+				}
+			}
+			if(cr["url"]){
+				cr._originalAction = fn.getAttribute("action");
+				fn.setAttribute("action", cr.url);
+			}
+			if(!fn.getAttribute("method")){
+				fn.setAttribute("method", (cr["method"]) ? cr["method"] : "post");
+			}
+			cr._originalTarget = fn.getAttribute("target");
+			fn.setAttribute("target", this.iframeName);
+			fn.target = this.iframeName;
+			fn.submit();
+		}else{
+			// otherwise we post a GET string by changing URL location for the
+			// iframe
+			var query = dojo.io.argsFromMap(this.currentRequest.content);
+			var tmpUrl = (cr.url.indexOf("?") > -1 ? "&" : "?") + query;
+			dojo.io.setIFrameSrc(this.iframe, tmpUrl, true);
+		}
+	}
+
+	this.canHandle = function(kwArgs){
+		return (
+			(
+				// FIXME: can we really handle text/plain and
+				// text/javascript requests?
+				dojo.lang.inArray(kwArgs["mimetype"], 
+				[	"text/plain", "text/html", 
+					"text/javascript", "text/json"])
+			)&&(
+				// make sur we really only get used in file upload cases	
+				(kwArgs["formNode"])&&(dojo.io.checkChildrenForFile(kwArgs["formNode"]))
+			)&&(
+				dojo.lang.inArray(kwArgs["method"].toLowerCase(), ["post", "get"])
+			)&&(
+				// never handle a sync request
+				!  ((kwArgs["sync"])&&(kwArgs["sync"] == true))
+			)
+		);
+	}
+
+	this.bind = function(kwArgs){
+		if(!this["iframe"]){ this.setUpIframe(); }
+		this.requestQueue.push(kwArgs);
+		this.fireNextRequest();
+		return;
+	}
+
+	this.setUpIframe = function(){
+
+		// NOTE: IE 5.0 and earlier Mozilla's don't support an onload event for
+		//       iframes. OTOH, we don't care.
+		this.iframe = dojo.io.createIFrame(this.iframeName, "dojo.io.IframeTransport.iframeOnload();");
+	}
+
+	this.iframeOnload = function(){
+		if(!_this.currentRequest){
+			_this.fireNextRequest();
+			return;
+		}
+
+		var req = _this.currentRequest;
+
+		// remove all the hidden content inputs
+		var toClean = req._contentToClean;
+		for(var i = 0; i < toClean.length; i++) {
+			var key = toClean[i];
+			if(dojo.render.html.safari){
+				//In Safari (at least 2.0.3), can't use formNode[key] syntax to find the node,
+				//for nodes that were dynamically added.
+				var fNode = req.formNode;
+				for(var j = 0; j < fNode.childNodes.length; j++){
+					var chNode = fNode.childNodes[j];
+					if(chNode.name == key){
+						var pNode = chNode.parentNode;
+						pNode.removeChild(chNode);
+						break;
+					}
+				}
+			}else{
+				var input = req.formNode[key];
+				req.formNode.removeChild(input);
+				req.formNode[key] = null;
+			}
+		}
+
+		// restore original action + target
+		if(req["_originalAction"]){
+			req.formNode.setAttribute("action", req._originalAction);
+		}
+		req.formNode.setAttribute("target", req._originalTarget);
+		req.formNode.target = req._originalTarget;
+
+		var ifd = dojo.io.iframeContentDocument(_this.iframe);
+		// handle successful returns
+		// FIXME: how do we determine success for iframes? Is there an equiv of
+		// the "status" property?
+		var value;
+		var success = false;
+
+		try{
+			var cmt = req.mimetype;
+			if((cmt == "text/javascript")||(cmt == "text/json")){
+				// FIXME: not sure what to do here? try to pull some evalulable
+				// text from a textarea or cdata section? 
+				// how should we set up the contract for that?
+				var js = ifd.getElementsByTagName("textarea")[0].value;
+				if(cmt == "text/json") { js = "(" + js + ")"; }
+				value = dj_eval(js);
+			}else if(cmt == "text/html"){
+				value = ifd;
+			}else{ // text/plain
+				value = ifd.getElementsByTagName("textarea")[0].value;
+			}
+			success = true;
+		}catch(e){ 
+			// looks like we didn't get what we wanted!
+			var errObj = new dojo.io.Error("IframeTransport Error");
+			if(dojo.lang.isFunction(req["error"])){
+				req.error("error", errObj, req);
+			}
+		}
+
+		// don't want to mix load function errors with processing errors, thus
+		// a separate try..catch
+		try {
+			if(success && dojo.lang.isFunction(req["load"])){
+				req.load("load", value, req);
+			}
+		} catch(e) {
+			throw e;
+		} finally {
+			_this.currentRequest = null;
+			_this.fireNextRequest();
+		}
+	}
+
+	dojo.io.transports.addTransport("IframeTransport");
+}


Property changes on: trunk/PhotoOp/root/static/js/src/io/IframeIO.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/io/RepubsubIO.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/io/RepubsubIO.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/io/RepubsubIO.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,517 @@
+//	Copyright (c) 2004 Friendster Inc., Licensed under the Academic Free
+//	License version 2.0 or later 
+
+dojo.require("dojo.event.Event");
+dojo.require("dojo.event.BrowserEvent");
+dojo.require("dojo.io.BrowserIO");
+
+dojo.provide("dojo.io.RepubsubIO");
+dojo.provide("dojo.io.repubsub");
+dojo.provide("dojo.io.repubsubTransport");
+
+dojo.io.repubsubTranport = new function(){
+	var rps = dojo.io.repubsub;
+	this.canHandle = function(kwArgs){
+		if((kwArgs["mimetype"] == "text/javascript")&&(kwArgs["method"] == "repubsub")){
+			return true;
+		}
+		return false;
+	}
+
+	this.bind = function(kwArgs){
+		if(!rps.isInitialized){
+			// open up our tunnel, queue up requests anyway
+			rps.init();
+		}
+		// FIXME: we need to turn this into a topic subscription
+		// var tgtURL = kwArgs.url+"?"+dojo.io.argsFromMap(kwArgs.content);
+		// sampleTransport.sendRequest(tgtURL, hdlrFunc);
+
+		// a normal "bind()" call in a request-response transport layer is
+		// something that (usually) encodes most of it's payload with the
+		// request. Multi-event systems like repubsub are a bit more complex,
+		// and repubsub in particular distinguishes the publish and subscribe
+		// portions of thep rocess with different method calls to handle each.
+		// Therefore, a "bind" in the sense of repubsub must first determine if
+		// we have an open subscription to a channel provided by the server,
+		// and then "publish" the request payload if there is any. We therefore
+		// must take care not to incorrectly or too agressively register or
+		// file event handlers which are provided with the kwArgs method.
+
+		// NOTE: we ONLY pay attention to those event handlers that are
+		// registered with the bind request that subscribes to the channel. If
+		// event handlers are provided with subsequent requests, we might in
+		// the future support some additive or replacement syntax, but for now
+		// they get dropped on the floor.
+
+		// NOTE: in this case, url MUST be the "topic" to which we
+		// subscribe/publish for this channel
+		if(!rps.topics[kwArgs.url]){
+			kwArgs.rpsLoad = function(evt){
+				kwArgs.load("load", evt);
+			}
+			rps.subscribe(kwArgs.url, kwArgs, "rpsLoad");
+		}
+
+		if(kwArgs["content"]){
+			// what we wanted to send
+			var cEvt = dojo.io.repubsubEvent.initFromProperties(kwArgs.content);
+			rps.publish(kwArgs.url, cEvt);
+		}
+	}
+
+	dojo.io.transports.addTransport("repubsubTranport");
+}
+
+dojo.io.repubsub = new function(){
+	this.initDoc = "init.html";
+	this.isInitialized = false;
+	this.subscriptionBacklog = [];
+	this.debug = true;
+	this.rcvNodeName = null;
+	this.sndNodeName = null;
+	this.rcvNode = null;
+	this.sndNode = null;
+	this.canRcv = false;
+	this.canSnd = false;
+	this.canLog = false;
+	this.sndTimer = null;
+	this.windowRef = window;
+	this.backlog = [];
+	this.tunnelInitCount = 0;
+	this.tunnelFrameKey = "tunnel_frame";
+	this.serverBaseURL = location.protocol+"//"+location.host+location.pathname;
+	this.logBacklog = [];
+	this.getRandStr = function(){
+		return Math.random().toString().substring(2, 10);
+	}
+	this.userid = "guest";
+	this.tunnelID = this.getRandStr();
+	this.attachPathList = [];
+	this.topics = []; // list of topics we have listeners to
+
+	// actually, now that I think about it a little bit more, it would sure be
+	// useful to parse out the <script> src attributes. We're looking for
+	// something with a "do_method=lib", since that's what would have included
+	// us in the first place (in the common case).
+	this.parseGetStr = function(){
+		var baseUrl = document.location.toString();
+		var params = baseUrl.split("?", 2);
+		if(params.length > 1){
+			var paramStr = params[1];
+			var pairs = paramStr.split("&");
+			var opts = [];
+			for(var x in pairs){
+				var sp = pairs[x].split("=");
+				// FIXME: is this eval dangerous?
+				try{
+					opts[sp[0]]=eval(sp[1]);
+				}catch(e){
+					opts[sp[0]]=sp[1];
+				}
+			}
+			return opts;
+		}else{
+			return [];
+		}
+	}
+
+	// parse URL params and use them as default vals
+	var getOpts = this.parseGetStr();
+	for(var x in getOpts){
+		// FIXME: should I be checking for undefined here before setting? Does
+		//        that buy me anything?
+		this[x] = getOpts[x];
+	}
+
+	if(!this["tunnelURI"]){
+		this.tunnelURI = [	"/who/", escape(this.userid), "/s/", 
+							this.getRandStr(), "/kn_journal"].join("");
+		// this.tunnelURI = this.absoluteTopicURI(this.tunnelURI);
+	}
+
+	/*
+	if (self.kn_tunnelID) kn.tunnelID = self.kn_tunnelID; // the server says
+	if (kn._argv.kn_tunnelID) kn.tunnelID = kn._argv.kn_tunnelID; // the url says
+	*/
+
+	// check the options object if it exists and use its properties as an
+	// over-ride
+	if(window["repubsubOpts"]||window["rpsOpts"]){
+		var optObj = window["repubsubOpts"]||window["rpsOpts"];
+		for(var x in optObj){
+			this[x] = optObj[x]; // copy the option object properties
+		}
+	}
+
+	// things that get called directly from our iframe to inform us of events
+	this.tunnelCloseCallback = function(){
+		// when we get this callback, we should immediately attempt to re-start
+		// our tunnel connection
+		dojo.io.setIFrameSrc(this.rcvNode, this.initDoc+"?callback=repubsub.rcvNodeReady&domain="+document.domain);
+	}
+
+	this.receiveEventFromTunnel = function(evt, srcWindow){
+		// we should never be getting events from windows we didn't create
+		// NOTE: events sourced from the local window are also supported for
+		// 		 debugging purposes
+
+		// any event object MUST have a an "elements" property
+		if(!evt["elements"]){
+			this.log("bailing! event received without elements!", "error");
+			return;
+		}
+
+		// if the event passes some minimal sanity tests, we need to attempt to
+		// dispatch it!
+
+		// first, it seems we have to munge the event object a bit
+		var e = {};
+		for(var i=0; i<evt.elements.length; i++){
+			var ee = evt.elements[i];
+			e[ee.name||ee.nameU] = (ee.value||ee.valueU);
+			// FIXME: need to enable this only in some extreme debugging mode!
+			this.log("[event]: "+(ee.name||ee.nameU)+": "+e[ee.name||ee.nameU]);
+		}
+
+		// NOTE: the previous version of this library put a bunch of code here
+		// to manage state that tried to make sure that we never, ever, lost
+		// any info about an event. If we unload RIGHT HERE, I don't think it's
+		// going to make a huge difference one way or another. Time will tell.
+
+		// and with THAT out of the way, dispatch it!
+		this.dispatch(e);
+
+		// TODO: remove the script block that created the event obj to save
+		// memory, etc.
+	}
+
+	this.widenDomain = function(domainStr){
+		// the purpose of this is to set the most liberal domain policy
+		// available
+		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(".");
+	}
+
+	// FIXME: parseCookie and setCookie should be methods that are more broadly
+	// available. Perhaps in htmlUtils?
+
+	this.parseCookie = function(){
+		var cs = document.cookie;
+		var keypairs = cs.split(";");
+		for(var x=0; x<keypairs.length; x++){
+			keypairs[x] = keypairs[x].split("=");
+			if(x!=keypairs.length-1){ cs+=";"; }
+		}
+		return keypairs;
+	}
+
+	this.setCookie = function(keypairs, clobber){
+		// NOTE: we want to only ever set session cookies, so never provide an
+		// 		 expires date
+		if((clobber)&&(clobber==true)){ document.cookie = ""; }
+		var cs = "";
+		for(var x=0; x<keypairs.length; x++){
+			cs += keypairs[x][0]+"="+keypairs[x][1];
+			if(x!=keypairs.length-1){ cs+=";"; }
+		}
+		document.cookie = cs;
+	}
+
+	// FIXME: need to replace w/ dojo.log.*
+	this.log = function(str, lvl){
+		if(!this.debug){ return; } // we of course only care if we're in debug mode
+		while(this.logBacklog.length>0){
+			if(!this.canLog){ break; }
+			var blo = this.logBacklog.shift();
+			this.writeLog("["+blo[0]+"]: "+blo[1], blo[2]);
+		}
+		this.writeLog(str, lvl);
+	}
+
+	this.writeLog = function(str, lvl){
+		dojo.debug(((new Date()).toLocaleTimeString())+": "+str);
+	}
+
+	this.init = function(){
+		this.widenDomain();
+		// this.findPeers();
+		this.openTunnel();
+		this.isInitialized = true;
+		// FIXME: this seems like entirely the wrong place to replay the backlog
+		while(this.subscriptionBacklog.length){
+			this.subscribe.apply(this, this.subscriptionBacklog.shift());
+		}
+	}
+
+	this.clobber = function(){
+		if(this.rcvNode){
+			this.setCookie( [
+					[this.tunnelFrameKey,"closed"],
+					["path","/"]
+				], false 
+			);
+		}
+	}
+
+	this.openTunnel = function(){
+		// We create two iframes here:
+
+		// one for getting data
+		this.rcvNodeName = "rcvIFrame_"+this.getRandStr();
+		// set cookie that can be used to find the receiving iframe
+		this.setCookie( [
+				[this.tunnelFrameKey,this.rcvNodeName],
+				["path","/"]
+			], false
+		);
+
+		this.rcvNode = dojo.io.createIFrame(this.rcvNodeName);
+		// FIXME: set the src attribute here to the initialization URL
+		dojo.io.setIFrameSrc(this.rcvNode, this.initDoc+"?callback=repubsub.rcvNodeReady&domain="+document.domain);
+
+		// the other for posting data in reply
+
+		this.sndNodeName = "sndIFrame_"+this.getRandStr();
+		this.sndNode = dojo.io.createIFrame(this.sndNodeName);
+		// FIXME: set the src attribute here to the initialization URL
+		dojo.io.setIFrameSrc(this.sndNode, this.initDoc+"?callback=repubsub.sndNodeReady&domain="+document.domain);
+
+	}
+
+	this.rcvNodeReady = function(){
+		// FIXME: why is this sequence number needed? Why isn't the UID gen
+		// 		  function enough?
+        var statusURI = [this.tunnelURI, '/kn_status/', this.getRandStr(), '_', 
+						 String(this.tunnelInitCount++)].join(""); 
+            // (kn._seqNum++); // FIXME: !!!!
+		// this.canRcv = true;
+		this.log("rcvNodeReady");
+		// FIXME: initialize receiver and request the base topic
+		// dojo.io.setIFrameSrc(this.rcvNode, this.serverBaseURL+"/kn?do_method=blank");
+		var initURIArr = [	this.serverBaseURL, "/kn?kn_from=", escape(this.tunnelURI),
+							"&kn_id=", escape(this.tunnelID), "&kn_status_from=", 
+							escape(statusURI)];
+		// FIXME: does the above really need a kn_response_flush? won't the
+		// 		  server already know? If not, what good is it anyway?
+		dojo.io.setIFrameSrc(this.rcvNode, initURIArr.join(""));
+
+		// setup a status path listener, but don't tell the server about it,
+		// since it already knows we're itnerested in our own tunnel status
+		this.subscribe(statusURI, this, "statusListener", true);
+
+		this.log(initURIArr.join(""));
+	}
+
+	this.sndNodeReady = function(){
+		this.canSnd = true;
+		this.log("sndNodeReady");
+		this.log(this.backlog.length);
+		// FIXME: handle any pent-up send commands
+		if(this.backlog.length > 0){
+			this.dequeueEvent();
+		}
+	}
+
+	this.statusListener = function(evt){
+		this.log("status listener called");
+		this.log(evt.status, "info");
+	}
+
+	// this handles local event propigation
+	this.dispatch = function(evt){
+		// figure out what topic it came from
+		if(evt["to"]||evt["kn_routed_from"]){
+			var rf = evt["to"]||evt["kn_routed_from"];
+			// split off the base server URL
+			var topic = rf.split(this.serverBaseURL, 2)[1];
+			if(!topic){
+				// FIXME: how do we recover when we don't get a sane "from"? Do
+				// we try to route to it anyway?
+				topic = rf;
+			}
+			this.log("[topic] "+topic);
+			if(topic.length>3){
+				if(topic.slice(0, 3)=="/kn"){
+					topic = topic.slice(3);
+				}
+			}
+			if(this.attachPathList[topic]){
+				this.attachPathList[topic](evt);
+			}
+		}
+	}
+
+	this.subscribe = function(	topic /* kn_from in the old terminilogy */, 
+								toObj, toFunc, dontTellServer){
+		if(!this.isInitialized){
+			this.subscriptionBacklog.push([topic, toObj, toFunc, dontTellServer]);
+			return;
+		}
+		if(!this.attachPathList[topic]){
+			this.attachPathList[topic] = function(){ return true; }
+			this.log("subscribing to: "+topic);
+			this.topics.push(topic);
+		}
+		var revt = new dojo.io.repubsubEvent(this.tunnelURI, topic, "route");
+		var rstr = [this.serverBaseURL+"/kn", revt.toGetString()].join("");
+		dojo.event.kwConnect({
+			once: true,
+			srcObj: this.attachPathList, 
+			srcFunc: topic, 
+			adviceObj: toObj, 
+			adviceFunc: toFunc
+		});
+		// NOTE: the above is a local mapping, if we're not the leader, we
+		// 		 should connect our mapping to the topic handler of the peer
+		// 		 leader, this ensures that not matter what happens to the
+		// 		 leader, we don't really loose our heads if/when the leader
+		// 		 goes away.
+		if(!this.rcvNode){ /* this should be an error! */ }
+		if(dontTellServer){
+			return;
+		}
+		this.log("sending subscription to: "+topic);
+		// create a subscription event object and give it all the props we need
+		// to updates on the specified topic
+
+		// FIXME: we should only enqueue if this is our first subscription!
+		this.sendTopicSubToServer(topic, rstr);
+	}
+
+	this.sendTopicSubToServer = function(topic, str){
+		if(!this.attachPathList[topic]["subscriptions"]){
+			this.enqueueEventStr(str);
+			this.attachPathList[topic].subscriptions = 0;
+		}
+		this.attachPathList[topic].subscriptions++;
+	}
+
+	this.unSubscribe = function(topic, toObj, toFunc){
+		// first, locally disconnect
+		dojo.event.kwDisconnect({
+			srcObj: this.attachPathList, 
+			srcFunc: topic, 
+			adviceObj: toObj, 
+			adviceFunc: toFunc
+		});
+		
+		// FIXME: figure out if there are any remaining listeners to the topic,
+		// 		  and if not, inform the server of our desire not to be
+		// 		  notified of updates to the topic
+	}
+
+	// the "publish" method is really a misnomer, since it really means "take
+	// this event and send it to the server". Note that the "dispatch" method
+	// handles local event promigulation, and therefore we emulate both sides
+	// of a real event router without having to swallow all of the complexity.
+	this.publish = function(topic, event){
+		var evt = dojo.io.repubsubEvent.initFromProperties(event);
+		// FIXME: need to make sure we have from and to set correctly
+		// 		  before we serialize and send off to the great blue
+		// 		  younder.
+		evt.to = topic;
+		// evt.from = this.tunnelURI;
+
+		var evtURLParts = [];
+		evtURLParts.push(this.serverBaseURL+"/kn");
+
+		// serialize the event to a string and then post it to the correct
+		// topic
+		evtURLParts.push(evt.toGetString());
+		this.enqueueEventStr(evtURLParts.join(""));
+	}
+
+	this.enqueueEventStr = function(evtStr){
+		this.log("enqueueEventStr");
+		this.backlog.push(evtStr);
+		this.dequeueEvent();
+	}
+
+	this.dequeueEvent = function(force){
+		this.log("dequeueEvent");
+		if(this.backlog.length <= 0){ return; }
+		if((this.canSnd)||(force)){
+			dojo.io.setIFrameSrc(this.sndNode, this.backlog.shift()+"&callback=repubsub.sndNodeReady");
+			this.canSnd = false;
+		}else{
+			this.log("sndNode not available yet!", "debug");
+		}
+	}
+}
+
+dojo.io.repubsubEvent = function(to, from, method, id, routeURI, payload, dispname, uid){
+	this.to = to;
+	this.from = from;
+	this.method = method||"route";
+	this.id = id||repubsub.getRandStr();
+	this.uri = routeURI;
+	this.displayname = dispname||repubsub.displayname;
+	this.userid = uid||repubsub.userid;
+	this.payload = payload||"";
+	this.flushChars = 4096;
+
+	this.initFromProperties = function(evt){
+		if(evt.constructor = dojo.io.repubsubEvent){ 
+			for(var x in evt){
+				this[x] = evt[x];
+			}
+		}else{
+			// we want to copy all the properties of the evt object, and transform
+			// those that are "stock" properties of dojo.io.repubsubEvent. All others should
+			// be copied as-is
+			for(var x in evt){
+				if(typeof this.forwardPropertiesMap[x] == "string"){
+					this[this.forwardPropertiesMap[x]] = evt[x];
+				}else{
+					this[x] = evt[x];
+				}
+			}
+		}
+	}
+
+	this.toGetString = function(noQmark){
+		var qs = [ ((noQmark) ? "" : "?") ];
+		for(var x=0; x<this.properties.length; x++){
+			var tp = this.properties[x];
+			if(this[tp[0]]){
+				qs.push(tp[1]+"="+encodeURIComponent(String(this[tp[0]])));
+			}
+			// FIXME: we need to be able to serialize non-stock properties!!!
+		}
+		return qs.join("&");
+	}
+
+}
+
+dojo.io.repubsubEvent.prototype.properties = [["from", "kn_from"], ["to", "kn_to"], 
+									["method", "do_method"], ["id", "kn_id"], 
+									["uri", "kn_uri"], 
+									["displayname", "kn_displayname"], 
+									["userid", "kn_userid"], 
+									["payload", "kn_payload"],
+									["flushChars", "kn_response_flush"],
+									["responseFormat", "kn_response_format"] ];
+
+// maps properties from their old names to their new names...
+dojo.io.repubsubEvent.prototype.forwardPropertiesMap = {};
+// ...and vice versa...
+dojo.io.repubsubEvent.prototype.reversePropertiesMap = {};
+
+// and we then populate them both from the properties list
+for(var x=0; x<dojo.io.repubsubEvent.prototype.properties.length; x++){
+	var tp = dojo.io.repubsubEvent.prototype.properties[x];
+	dojo.io.repubsubEvent.prototype.reversePropertiesMap[tp[0]] = tp[1];
+	dojo.io.repubsubEvent.prototype.forwardPropertiesMap[tp[1]] = tp[0];
+}
+// static version of initFromProperties, creates new event and object and
+// returns it after init
+dojo.io.repubsubEvent.initFromProperties = function(evt){
+	var eventObj = new dojo.io.repubsubEvent();
+	eventObj.initFromProperties(evt);
+	return eventObj;
+}


Property changes on: trunk/PhotoOp/root/static/js/src/io/RepubsubIO.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/io/RhinoIO.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/io/RhinoIO.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/io/RhinoIO.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,22 @@
+/*
+	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.io.RhinoIO");
+
+// TODO: this doesn't execute
+/*dojo.io.SyncHTTPRequest = function(){
+	dojo.io.SyncRequest.call(this);
+
+	this.send = function(URI){
+	}
+}
+
+dojo.inherits(dojo.io.SyncHTTPRequest, dojo.io.SyncRequest);
+*/


Property changes on: trunk/PhotoOp/root/static/js/src/io/RhinoIO.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/io/ScriptSrcIO.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/io/ScriptSrcIO.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/io/ScriptSrcIO.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,452 @@
+/*
+	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.io.ScriptSrcIO");
+dojo.require("dojo.io.BrowserIO");
+dojo.require("dojo.undo.browser");
+
+//FIXME: should constantParams be JS object?
+//FIXME: check dojo.io calls. Can we move the BrowserIO defined calls somewhere
+//       else so that we don't depend on BrowserIO at all? The dependent calls
+//       have to do with dealing with forms and making query params from JS object.
+/**
+ * See test_ScriptSrcIO.html for usage information.
+ * Notes:
+ * - The watchInFlight timer is set to 100 ms instead of 10ms (which is what BrowserIO.js uses).
+ */
+dojo.io.ScriptSrcTransport = new function(){
+	this.preventCache = false; // if this is true, we'll always force GET requests to not cache
+	this.maxUrlLength = 1000; //Used to calculate if script request should be multipart.
+	this.inFlightTimer = null;
+
+	this.DsrStatusCodes = {
+		Continue: 100,
+		Ok: 200,
+		Error: 500
+	};
+
+	this.startWatchingInFlight = function(){
+		if(!this.inFlightTimer){
+			this.inFlightTimer = setInterval("dojo.io.ScriptSrcTransport.watchInFlight();", 100);
+		}
+	}
+
+	this.watchInFlight = function(){
+		var totalCount = 0;
+		var doneCount = 0;
+		for(var param in this._state){
+			totalCount++;
+			var currentState = this._state[param];
+			if(currentState.isDone){
+				doneCount++;
+				delete this._state[param];
+			}else{
+				var listener = currentState.kwArgs;
+				try{
+					if(currentState.checkString && eval("typeof(" + currentState.checkString + ") != 'undefined'")){
+						this._finish(currentState, "load");
+						doneCount++;
+						delete this._state[param];
+					}else if(listener.timeoutSeconds && listener.timeout){
+						if(currentState.startTime + (listener.timeoutSeconds * 1000) < (new Date()).getTime()){
+							this._finish(currentState, "timeout");
+							doneCount++;
+							delete this._state[param];
+						}
+					}else if(!listener.timeoutSeconds){
+						//Increment the done count if no timeout is specified, so
+						//that we turn off the timer if all that is left in the state
+						//list are things we can't clean up because they fail without
+						//getting a callback.
+						doneCount++;
+					}
+				}catch(e){
+					this._finish(currentState, "error", {status: this.DsrStatusCodes.Error, response: e});
+				}
+			}
+		}
+	
+		if(doneCount == totalCount){
+			clearInterval(this.inFlightTimer);
+			this.inFlightTimer = null;
+		}
+	}
+
+	this.canHandle = function(kwArgs){
+		return dojo.lang.inArray((kwArgs["mimetype"].toLowerCase()), ["text/javascript", "text/json"])
+			&& (kwArgs["method"].toLowerCase() == "get")
+			&& !(kwArgs["formNode"] && dojo.io.formHasFile(kwArgs["formNode"]))
+			&& (!kwArgs["sync"] || kwArgs["sync"] == false)
+			&& !kwArgs["file"]
+			&& !kwArgs["multipart"];
+	}
+
+	/**
+	 * Removes any script tags from the DOM that may have been added by ScriptSrcTransport.
+	 * Be careful though, by removing them from the script, you may invalidate some
+	 * script objects that were defined by the js file that was pulled in as the
+	 * src of the script tag. Test carefully if you decide to call this method.
+	 * 
+	 * In MSIE 6 (and probably 5.x), if you removed the script element while 
+	 * part of the script is still executing, the browser will crash.
+	 */
+	this.removeScripts = function(){
+		var scripts = document.getElementsByTagName("script");
+		for(var i = 0; scripts && i < scripts.length; i++){
+			var scriptTag = scripts[i];
+			if(scriptTag.className == "ScriptSrcTransport"){
+				var parent = scriptTag.parentNode;
+				parent.removeChild(scriptTag);
+				i--; //Set the index back one since we removed an item.
+			}
+		}
+	}
+
+	this.bind = function(kwArgs){
+		//START duplication from BrowserIO.js (some changes made)
+		var url = kwArgs.url;
+		var query = "";
+		
+		if(kwArgs["formNode"]){
+			var ta = kwArgs.formNode.getAttribute("action");
+			if((ta)&&(!kwArgs["url"])){ url = ta; }
+			var tp = kwArgs.formNode.getAttribute("method");
+			if((tp)&&(!kwArgs["method"])){ kwArgs.method = tp; }
+			query += dojo.io.encodeForm(kwArgs.formNode, kwArgs.encoding, kwArgs["formFilter"]);
+		}
+
+		if(url.indexOf("#") > -1) {
+			dojo.debug("Warning: dojo.io.bind: stripping hash values from url:", url);
+			url = url.split("#")[0];
+		}
+
+		//Break off the domain/path of the URL.
+		var urlParts = url.split("?");
+		if(urlParts && urlParts.length == 2){
+			url = urlParts[0];
+			query += (query ? "&" : "") + urlParts[1];
+		}
+
+		if(kwArgs["backButton"] || kwArgs["back"] || kwArgs["changeUrl"]){
+			dojo.undo.browser.addToHistory(kwArgs);
+		}
+
+		//Create an ID for the request.
+		var id = kwArgs["apiId"] ? kwArgs["apiId"] : "id" + this._counter++;
+
+		//Fill out any other content pieces.
+		var content = kwArgs["content"];
+		var jsonpName = kwArgs.jsonParamName;
+		if(kwArgs.sendTransport || jsonpName) {
+			if (!content){
+				content = {};
+			}
+			if(kwArgs.sendTransport){
+				content["dojo.transport"] = "scriptsrc";
+			}
+
+			if(jsonpName){
+				content[jsonpName] = "dojo.io.ScriptSrcTransport._state." + id + ".jsonpCall";
+			}
+		}
+
+		if(kwArgs.postContent){
+			query = kwArgs.postContent;
+		}else if(content){
+			query += ((query) ? "&" : "") + dojo.io.argsFromMap(content, kwArgs.encoding, jsonpName);
+		}
+		//END duplication from BrowserIO.js
+
+		//START DSR
+
+		//If an apiId is specified, then we want to make sure useRequestId is true.
+		if(kwArgs["apiId"]){
+			kwArgs["useRequestId"] = true;
+		}
+
+		//Set up the state for this request.
+		var state = {
+			"id": id,
+			"idParam": "_dsrid=" + id,
+			"url": url,
+			"query": query,
+			"kwArgs": kwArgs,
+			"startTime": (new Date()).getTime()
+		};
+
+		if(!url){
+			//Error. An URL is needed.
+			this._finish(state, "error", {status: this.DsrStatusCodes.Error, statusText: "url.none"});
+			return;
+		}
+
+		//If this is a jsonp request, intercept the jsonp callback
+		if(content && content[jsonpName]){
+			state.jsonp = content[jsonpName];
+			state.jsonpCall = function(data){
+				if(data["Error"]||data["error"]){
+					dojo.debug(dojo.json.serialize(data));
+					dojo.io.ScriptSrcTransport._finish(this, "error", data);
+				}else{
+					dojo.io.ScriptSrcTransport._finish(this, "load", data);
+				}
+			};
+		}
+
+		//Only store the request state on the state tracking object if a callback
+		//is expected or if polling on a checkString will be done.
+		if(kwArgs["useRequestId"] || kwArgs["checkString"] || state["jsonp"]){
+			this._state[id] = state;
+		}
+
+		//A checkstring is a string that if evaled will not be undefined once the
+		//script src loads. Used as an alternative to depending on a callback from
+		//the script file. If this is set, then multipart is not assumed to be used,
+		//since multipart requires a specific callback. With checkString we will be doing
+		//polling.
+		if(kwArgs["checkString"]){
+			state.checkString = kwArgs["checkString"];
+		}
+
+		//Constant params are parameters that should always be sent with each
+		//part of a multipart URL.
+		state.constantParams = (kwArgs["constantParams"] == null ? "" : kwArgs["constantParams"]);
+	
+		if(kwArgs["preventCache"] ||
+			(this.preventCache == true && kwArgs["preventCache"] != false)){
+			state.nocacheParam = "dojo.preventCache=" + new Date().valueOf();
+		}else{
+			state.nocacheParam = "";
+		}
+
+		//Get total length URL, if we were to do it as one URL.
+		//Add some padding, extra & separators.
+		var urlLength = state.url.length + state.query.length + state.constantParams.length 
+				+ state.nocacheParam.length + this._extraPaddingLength;
+
+		if(kwArgs["useRequestId"]){
+			urlLength += state.idParam.length;
+		}
+		
+		if(!kwArgs["checkString"] && kwArgs["useRequestId"] 
+			&& !state["jsonp"] && !kwArgs["forceSingleRequest"]
+			&& urlLength > this.maxUrlLength){
+			if(url > this.maxUrlLength){
+				//Error. The URL domain and path are too long. We can't
+				//segment that, so return an error.
+				this._finish(state, "error", {status: this.DsrStatusCodes.Error, statusText: "url.tooBig"});
+				return;
+			}else{
+				//Start the multiple requests.
+				this._multiAttach(state, 1);
+			}
+		}else{
+			//Send one URL.
+			var queryParams = [state.constantParams, state.nocacheParam, state.query];
+			if(kwArgs["useRequestId"] && !state["jsonp"]){
+				queryParams.unshift(state.idParam);
+			}
+			var finalUrl = this._buildUrl(state.url, queryParams);
+
+			//Track the final URL in case we need to use that instead of api ID when receiving
+			//the load callback.
+			state.finalUrl = finalUrl;
+			
+			this._attach(state.id, finalUrl);
+		}
+		//END DSR
+
+		this.startWatchingInFlight();
+	}
+	
+	//Private properties/methods
+	this._counter = 1;
+	this._state = {};
+	this._extraPaddingLength = 16;
+
+	//Is there a dojo function for this already?
+	this._buildUrl = function(url, nameValueArray){
+		var finalUrl = url;
+		var joiner = "?";
+		for(var i = 0; i < nameValueArray.length; i++){
+			if(nameValueArray[i]){
+				finalUrl += joiner + nameValueArray[i];
+				joiner = "&";
+			}
+		}
+
+		return finalUrl;
+	}
+
+	this._attach = function(id, url){
+		//Attach the script to the DOM.
+		var element = document.createElement("script");
+		element.type = "text/javascript";
+		element.src = url;
+		element.id = id;
+		element.className = "ScriptSrcTransport";
+		document.getElementsByTagName("head")[0].appendChild(element);
+	}
+
+	this._multiAttach = function(state, part){
+		//Check to make sure we still have a query to send up. This is mostly
+		//a protection from a goof on the server side when it sends a part OK
+		//response instead of a final response.
+		if(state.query == null){
+			this._finish(state, "error", {status: this.DsrStatusCodes.Error, statusText: "query.null"});
+			return;
+		}
+
+		if(!state.constantParams){
+			state.constantParams = "";
+		}
+
+		//How much of the query can we take?
+		//Add a padding constant to account for _part and a couple extra amperstands.
+		//Also add space for id since we'll need it now.
+		var queryMax = this.maxUrlLength - state.idParam.length
+					 - state.constantParams.length - state.url.length
+					 - state.nocacheParam.length - this._extraPaddingLength;
+		
+		//Figure out if this is the last part.
+		var isDone = state.query.length < queryMax;
+	
+		//Break up the query string if necessary.
+		var currentQuery;
+		if(isDone){
+			currentQuery = state.query;
+			state.query = null;
+		}else{
+			//Find the & or = nearest the max url length.
+			var ampEnd = state.query.lastIndexOf("&", queryMax - 1);
+			var eqEnd = state.query.lastIndexOf("=", queryMax - 1);
+
+			//See if & is closer, or if = is right at the edge,
+			//which means we should put it on the next URL.
+			if(ampEnd > eqEnd || eqEnd == queryMax - 1){
+				//& is nearer the end. So just chop off from there.
+				currentQuery = state.query.substring(0, ampEnd);
+				state.query = state.query.substring(ampEnd + 1, state.query.length) //strip off amperstand with the + 1.
+			}else{
+				//= is nearer the end. Take the max amount possible. 
+				currentQuery = state.query.substring(0, queryMax);
+			 
+				//Find the last query name in the currentQuery so we can prepend it to
+				//ampEnd. Could be -1 (not there), so account for that.
+				var queryName = currentQuery.substring((ampEnd == -1 ? 0 : ampEnd + 1), eqEnd);
+				state.query = queryName + "=" + state.query.substring(queryMax, state.query.length);
+			}
+		}
+		
+		//Now send a part of the script
+		var queryParams = [currentQuery, state.idParam, state.constantParams, state.nocacheParam];
+		if(!isDone){
+			queryParams.push("_part=" + part);
+		}
+
+		var url = this._buildUrl(state.url, queryParams);
+
+		this._attach(state.id + "_" + part, url);
+	}
+
+	this._finish = function(state, callback, event){
+		if(callback != "partOk" && !state.kwArgs[callback] && !state.kwArgs["handle"]){
+			//Ignore "partOk" because that is an internal callback.
+			if(callback == "error"){
+				state.isDone = true;
+				throw event;
+			}
+		}else{
+			switch(callback){
+				case "load":
+					var response = event ? event.response : null;
+					if(!response){
+						response = event;
+					}
+					state.kwArgs[(typeof state.kwArgs.load == "function") ? "load" : "handle"]("load", response, event, state.kwArgs);
+					state.isDone = true;
+					break;
+				case "partOk":
+					var part = parseInt(event.response.part, 10) + 1;
+					//Update the constant params, if any.
+					if(event.response.constantParams){
+						state.constantParams = event.response.constantParams;
+					}
+					this._multiAttach(state, part);
+					state.isDone = false;
+					break;
+				case "error":
+					state.kwArgs[(typeof state.kwArgs.error == "function") ? "error" : "handle"]("error", event.response, event, state.kwArgs);
+					state.isDone = true;
+					break;
+				default:
+					state.kwArgs[(typeof state.kwArgs[callback] == "function") ? callback : "handle"](callback, event, event, state.kwArgs);
+					state.isDone = true;
+			}
+		}
+	}
+
+	dojo.io.transports.addTransport("ScriptSrcTransport");
+}
+
+//Define callback handler.
+window.onscriptload = function(event){
+	var state = null;
+	var transport = dojo.io.ScriptSrcTransport;
+	
+	//Find the matching state object for event ID.
+	if(transport._state[event.id]){
+		state = transport._state[event.id];
+	}else{
+		//The ID did not match directly to an entry in the state list.
+		//Try searching the state objects for a matching original URL.
+		var tempState;
+		for(var param in transport._state){
+			tempState = transport._state[param];
+			if(tempState.finalUrl && tempState.finalUrl == event.id){
+				state = tempState;
+				break;
+			}
+		}
+
+		//If no matching original URL is found, then use the URL that was actually used
+		//in the SCRIPT SRC attribute.
+		if(state == null){
+			var scripts = document.getElementsByTagName("script");
+			for(var i = 0; scripts && i < scripts.length; i++){
+				var scriptTag = scripts[i];
+				if(scriptTag.getAttribute("class") == "ScriptSrcTransport"
+					&& scriptTag.src == event.id){
+					state = transport._state[scriptTag.id];
+					break;
+				}
+			}
+		}
+		
+		//If state is still null, then throw an error.
+		if(state == null){
+			throw "No matching state for onscriptload event.id: " + event.id;
+		}
+	}
+
+	var callbackName = "error";
+	switch(event.status){
+		case dojo.io.ScriptSrcTransport.DsrStatusCodes.Continue:
+			//A part of a multipart request.
+			callbackName = "partOk";
+			break;
+		case dojo.io.ScriptSrcTransport.DsrStatusCodes.Ok:
+			//Successful reponse.
+			callbackName = "load";
+			break;
+	}
+
+	transport._finish(state, callbackName, event);
+};


Property changes on: trunk/PhotoOp/root/static/js/src/io/ScriptSrcIO.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/io/ShortBusIO.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/io/ShortBusIO.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/io/ShortBusIO.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,171 @@
+/*
+	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.io.ShortBusIO");
+dojo.require("dojo.io"); // io.js provides setIFrameSrc
+// 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"); // for posting across domains
+dojo.require("dojo.io.cookie"); // for peering
+dojo.require("dojo.event.*");
+
+/*
+ * this file defines a "forever-frame" style Comet client. It passes opaque
+ * JSON data structures to/from the client. Both styles of request provide a
+ * topic for the event to be sent to and a payload object to be acted upon.
+ *
+ * All outbound events are sent via dojo.io.bind() and all inbound requests are
+ * processed by Dojo topic dispatch.
+ *
+ * ShortBusIO packets have the basic format:
+ *
+ *	{
+ *	 	topic: "/destination/topic/name",
+ *		body: {
+ * 			// ...
+ *		}
+ * 	}
+ * 
+ * Packets bound for the event router (not one of it's clients) or generated
+ * from it are prefixed with the special "/meta" topic. Meta-topic events
+ * either inform the client to take an action or inform the server of a system
+ * event.
+ *
+ * Upon tunnel creation, the server might therefore send the following meta
+ * topic packet to the client to inform the client of it's assigned identity:
+ *
+ *	// client <-- server
+ *	{
+ *	 	topic: "/meta",
+ *		body: {
+ * 			action: "setClientId",
+ *			clientId: "fooBar23",
+ *			tunnelId: "fooBarTunnel4",
+ *			tunnelExpiration: "...", // some date in the future
+ *		}
+ * 	}
+ *
+ * The client may then respond with a confirmation:
+ * 
+ *	// client --> server
+ *	{
+ *	 	topic: "/meta",
+ *		body: {
+ * 			action: "confirmClientId",
+ *			from: "fooBar23"
+ *		}
+ * 	}
+ *
+ * The client must implement a basic vocabulary of /meta topic verbs in order
+ * to participate as a ShortBus endpoint. These are TBD.
+ *
+ * NOTE: this example elides any authentication or authorization steps the
+ * client and server may have undertaken prior to tunnel setup.
+ */
+
+// TODO: unlike repubsubio we don't handle any sort of connection
+// subscription/publishing backlog. Should we?
+
+dojo.io.ShortBusTransport = new function(){
+
+	var initialized = false;
+	var connected = false;
+
+	// this class is similar to RepubsubIO save that we don't have the
+	// externalized protocol handler code. Our messages are simpler so our code
+	// can be as well.
+
+	this.rcvNode = null;
+	this.rcvNodeName = "";
+	this.topicRoot = null;
+
+	this.getRandStr = function(){
+		return Math.random().toString().substring(2, 10);
+	}
+
+	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(".");
+	}
+
+	this.canHandle = function(kwArgs){
+		return (
+			(connected)			&&
+			(kwArgs["topic"])	&&
+			(! // async only!
+				((kwArgs["sync"])&&(kwArgs["sync"] == true))
+			)
+		);
+	}
+
+	this.buildConnection = function(){
+		// NOTE: we require the server to cooperate by hosting
+		// ShortBusInit.html at the designated endpoint
+		this.rcvNodeName = "ShortBusRcv_"+this.getRandStr();
+		// the "forever frame" approach
+		if(dojo.render.html.ie){
+			// use the "htmlfile hack" to prevent the background click junk
+			this.rcvNode = new ActiveXObject("htmlfile");
+			this.rcvNode.open();
+			this.rcvNode.write("<html>");
+			this.rcvNode.write("<script>document.domain = '"+document.domain+"'");
+			this.rcvNode.write("</html>");
+			this.rcvNode.close();
+
+			var ifrDiv = this.rcvNode.createElement("div");
+			this.rcvNode.appendChild(ifrDiv);
+			this.rcvNode.parentWindow.dojo = dojo;
+			ifrDiv.innerHTML = "<iframe src='"+this.topicRoot+"/?tunntelType=htmlfile'></iframe>"
+			// 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");
+}


Property changes on: trunk/PhotoOp/root/static/js/src/io/ShortBusIO.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/io/ShortBusInit.html
===================================================================
--- trunk/PhotoOp/root/static/js/src/io/ShortBusInit.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/io/ShortBusInit.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,75 @@
+<html>
+<!--
+	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
+-->
+<script type="text/javascript">
+	if(window!=window.parent){
+		function callByDeRef(fname){
+			if(!fname){ return null; }
+			// if someone inadvertently passed in "foo(...)", we make it "foo"
+			fname = String(fname).split("(")[0];
+			// get a real array of arguments
+			var aa = [];
+			for(var x=1; x<arguments.length; x++){
+				aa.push(arguments[x]);
+			}
+
+			var parts = String(fname).split(".");
+			var obj = window;
+			for(var x=0; x<parts.length-1; x++){
+				obj = obj[parts[x]];
+			}
+			var fn = parts.pop(); // the last element is the function name
+			// exec the function in the specified namespace
+			return obj[fn].apply(obj, aa);
+		}
+
+		function widenDomain(domainStr){
+			// the purpose of this is to set the most liberal domain policy
+			var cd = domainStr||document.domain;
+			if(cd.indexOf(".")==-1){ 
+				document.domain = cd;
+				return;
+			}
+			var dps = cd.split(".");
+			if(dps.length>2){ 
+				dps = dps.slice(dps.length-2);
+			}
+			document.domain = dps.join(".");
+		}
+
+		function doInit(){
+
+			widenDomain();
+
+			var baseUrl = document.location.toString();
+			var params = baseUrl.split("?", 2);
+			if(params.length > 1){
+				var paramStr = params[1];
+				var pairs = paramStr.split("&");
+				var opts = [];
+				for(var x in pairs){
+					// alert(pairs[x]);
+					var sp = pairs[x].split("=");
+					opts[sp[0]]=sp[1];
+					if(sp[0]=="true"){
+						sp[0] = true;
+					}else if(sp[0]=="false"){
+						sp[0] = false;
+					}
+				}
+				if(opts["callback"]){
+					callByDeRef("parent."+opts["callback"]);
+				}
+			}
+		}
+		doInit();
+	}
+</script>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/src/io/ShortBusInit.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/io/__package__.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/io/__package__.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/io/__package__.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,17 @@
+/*
+	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.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.*");


Property changes on: trunk/PhotoOp/root/static/js/src/io/__package__.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/io/cookie.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/io/cookie.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/io/cookie.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,108 @@
+/*
+	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.io.cookie");
+
+dojo.io.cookie.setCookie = function(name, value, days, path, domain, secure) {
+	var expires = -1;
+	if(typeof days == "number" && days >= 0) {
+		var d = new Date();
+		d.setTime(d.getTime()+(days*24*60*60*1000));
+		expires = d.toGMTString();
+	}
+	value = escape(value);
+	document.cookie = name + "=" + value + ";"
+		+ (expires != -1 ? " expires=" + expires + ";" : "")
+		+ (path ? "path=" + path : "")
+		+ (domain ? "; domain=" + domain : "")
+		+ (secure ? "; secure" : "");
+}
+
+dojo.io.cookie.set = dojo.io.cookie.setCookie;
+
+dojo.io.cookie.getCookie = function(name) {
+	// FIXME: Which cookie should we return?
+	//        If there are cookies set for different sub domains in the current
+	//        scope there could be more than one cookie with the same name.
+	//        I think taking the last one in the list takes the one from the
+	//        deepest subdomain, which is what we're doing here.
+	var idx = document.cookie.lastIndexOf(name+'=');
+	if(idx == -1) { return null; }
+	var value = document.cookie.substring(idx+name.length+1);
+	var end = value.indexOf(';');
+	if(end == -1) { end = value.length; }
+	value = value.substring(0, end);
+	value = unescape(value);
+	return value;
+}
+
+dojo.io.cookie.get = dojo.io.cookie.getCookie;
+
+dojo.io.cookie.deleteCookie = function(name) {
+	dojo.io.cookie.setCookie(name, "-", 0);
+}
+
+dojo.io.cookie.setObjectCookie = function(name, obj, days, path, domain, secure, clearCurrent) {
+	if(arguments.length == 5) { // for backwards compat
+		clearCurrent = domain;
+		domain = null;
+		secure = null;
+	}
+	var pairs = [], cookie, value = "";
+	if(!clearCurrent) { cookie = dojo.io.cookie.getObjectCookie(name); }
+	if(days >= 0) {
+		if(!cookie) { cookie = {}; }
+		for(var prop in obj) {
+			if(prop == null) {
+				delete cookie[prop];
+			} else if(typeof obj[prop] == "string" || typeof obj[prop] == "number") {
+				cookie[prop] = obj[prop];
+			}
+		}
+		prop = null;
+		for(var prop in cookie) {
+			pairs.push(escape(prop) + "=" + escape(cookie[prop]));
+		}
+		value = pairs.join("&");
+	}
+	dojo.io.cookie.setCookie(name, value, days, path, domain, secure);
+}
+
+dojo.io.cookie.getObjectCookie = function(name) {
+	var values = null, cookie = dojo.io.cookie.getCookie(name);
+	if(cookie) {
+		values = {};
+		var pairs = cookie.split("&");
+		for(var i = 0; i < pairs.length; i++) {
+			var pair = pairs[i].split("=");
+			var value = pair[1];
+			if( isNaN(value) ) { value = unescape(pair[1]); }
+			values[ unescape(pair[0]) ] = value;
+		}
+	}
+	return values;
+}
+
+dojo.io.cookie.isSupported = function() {
+	if(typeof navigator.cookieEnabled != "boolean") {
+		dojo.io.cookie.setCookie("__TestingYourBrowserForCookieSupport__",
+			"CookiesAllowed", 90, null);
+		var cookieVal = dojo.io.cookie.getCookie("__TestingYourBrowserForCookieSupport__");
+		navigator.cookieEnabled = (cookieVal == "CookiesAllowed");
+		if(navigator.cookieEnabled) {
+			// FIXME: should we leave this around?
+			this.deleteCookie("__TestingYourBrowserForCookieSupport__");
+		}
+	}
+	return navigator.cookieEnabled;
+}
+
+// need to leave this in for backwards-compat from 0.1 for when it gets pulled in by dojo.io.*
+if(!dojo.io.cookies) { dojo.io.cookies = dojo.io.cookie; }


Property changes on: trunk/PhotoOp/root/static/js/src/io/cookie.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/io/cookies.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/io/cookies.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/io/cookies.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,14 @@
+/*
+	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.io.cookies", "replaced by dojo.io.cookie", "0.4");
+dojo.require("dojo.io.cookie");
+if(!dojo.io.cookies) { dojo.io.cookies = dojo.io.cookie; }
+dojo.provide("dojo.io.cookies");


Property changes on: trunk/PhotoOp/root/static/js/src/io/cookies.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/io.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/io.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/io.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,376 @@
+/*
+	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.io.IO");
+dojo.require("dojo.string");
+dojo.require("dojo.lang.extras");
+
+/******************************************************************************
+ *	Notes about dojo.io design:
+ *	
+ *	The dojo.io.* package has the unenviable task of making a lot of different
+ *	types of I/O feel natural, despite a universal lack of good (or even
+ *	reasonable!) I/O capability in the host environment. So lets pin this down
+ *	a little bit further.
+ *
+ *	Rhino:
+ *		perhaps the best situation anywhere. Access to Java classes allows you
+ *		to do anything one might want in terms of I/O, both synchronously and
+ *		async. Can open TCP sockets and perform low-latency client/server
+ *		interactions. HTTP transport is available through Java HTTP client and
+ *		server classes. Wish it were always this easy.
+ *
+ *	xpcshell:
+ *		XPCOM for I/O. A cluster-fuck to be sure.
+ *
+ *	spidermonkey:
+ *		S.O.L.
+ *
+ *	Browsers:
+ *		Browsers generally do not provide any useable filesystem access. We are
+ *		therefore limited to HTTP for moving information to and from Dojo
+ *		instances living in a browser.
+ *
+ *		XMLHTTP:
+ *			Sync or async, allows reading of arbitrary text files (including
+ *			JS, which can then be eval()'d), writing requires server
+ *			cooperation and is limited to HTTP mechanisms (POST and GET).
+ *
+ *		<iframe> hacks:
+ *			iframe document hacks allow browsers to communicate asynchronously
+ *			with a server via HTTP POST and GET operations. With significant
+ *			effort and server cooperation, low-latency data transit between
+ *			client and server can be acheived via iframe mechanisms (repubsub).
+ *
+ *		SVG:
+ *			Adobe's SVG viewer implements helpful primitives for XML-based
+ *			requests, but receipt of arbitrary text data seems unlikely w/o
+ *			<![CDATA[]]> sections.
+ *
+ *
+ *	A discussion between Dylan, Mark, Tom, and Alex helped to lay down a lot
+ *	the IO API interface. A transcript of it can be found at:
+ *		http://dojotoolkit.org/viewcvs/viewcvs.py/documents/irc/irc_io_api_log.txt?rev=307&view=auto
+ *	
+ *	Also referenced in the design of the API was the DOM 3 L&S spec:
+ *		http://www.w3.org/TR/2004/REC-DOM-Level-3-LS-20040407/load-save.html
+ ******************************************************************************/
+
+// a map of the available transport options. Transports should add themselves
+// by calling add(name)
+dojo.io.transports = [];
+dojo.io.hdlrFuncNames = [ "load", "error", "timeout" ]; // we're omitting a progress() event for now
+
+dojo.io.Request = function(url, mimetype, transport, changeUrl){
+	if((arguments.length == 1)&&(arguments[0].constructor == Object)){
+		this.fromKwArgs(arguments[0]);
+	}else{
+		this.url = url;
+		if(mimetype){ this.mimetype = mimetype; }
+		if(transport){ this.transport = transport; }
+		if(arguments.length >= 4){ this.changeUrl = changeUrl; }
+	}
+}
+
+dojo.lang.extend(dojo.io.Request, {
+
+	/** The URL to hit */
+	url: "",
+	
+	/** The mime type used to interrpret the response body */
+	mimetype: "text/plain",
+	
+	/** The HTTP method to use */
+	method: "GET",
+	
+	/** An Object containing key-value pairs to be included with the request */
+	content: undefined, // Object
+	
+	/** The transport medium to use */
+	transport: undefined, // String
+	
+	/** If defined the URL of the page is physically changed */
+	changeUrl: undefined, // String
+	
+	/** A form node to use in the request */
+	formNode: undefined, // HTMLFormElement
+	
+	/** Whether the request should be made synchronously */
+	sync: false,
+	
+	bindSuccess: false,
+
+	/** Cache/look for the request in the cache before attempting to request?
+	 *  NOTE: this isn't a browser cache, this is internal and would only cache in-page
+	 */
+	useCache: false,
+
+	/** Prevent the browser from caching this by adding a query string argument to the URL */
+	preventCache: false,
+	
+	// events stuff
+	load: function(type, data, evt){ },
+	error: function(type, error){ },
+	timeout: function(type){ },
+	handle: function(){ },
+
+	//FIXME: change BrowserIO.js to use timeouts? IframeIO?
+	// The number of seconds to wait until firing a timeout callback.
+	// If it is zero, that means, don't do a timeout check.
+	timeoutSeconds: 0,
+	
+	// the abort method needs to be filled in by the transport that accepts the
+	// bind() request
+	abort: function(){ },
+	
+	// backButton: function(){ },
+	// forwardButton: function(){ },
+
+	fromKwArgs: function(kwArgs){
+		// normalize args
+		if(kwArgs["url"]){ kwArgs.url = kwArgs.url.toString(); }
+		if(kwArgs["formNode"]) { kwArgs.formNode = dojo.byId(kwArgs.formNode); }
+		if(!kwArgs["method"] && kwArgs["formNode"] && kwArgs["formNode"].method) {
+			kwArgs.method = kwArgs["formNode"].method;
+		}
+		
+		// backwards compatibility
+		if(!kwArgs["handle"] && kwArgs["handler"]){ kwArgs.handle = kwArgs.handler; }
+		if(!kwArgs["load"] && kwArgs["loaded"]){ kwArgs.load = kwArgs.loaded; }
+		if(!kwArgs["changeUrl"] && kwArgs["changeURL"]) { kwArgs.changeUrl = kwArgs.changeURL; }
+
+		// encoding fun!
+		kwArgs.encoding = dojo.lang.firstValued(kwArgs["encoding"], djConfig["bindEncoding"], "");
+
+		kwArgs.sendTransport = dojo.lang.firstValued(kwArgs["sendTransport"], djConfig["ioSendTransport"], false);
+
+		var isFunction = dojo.lang.isFunction;
+		for(var x=0; x<dojo.io.hdlrFuncNames.length; x++){
+			var fn = dojo.io.hdlrFuncNames[x];
+			if(isFunction(kwArgs[fn])){ continue; }
+			if(isFunction(kwArgs["handle"])){
+				kwArgs[fn] = kwArgs.handle;
+			}
+			// handler is aliased above, shouldn't need this check
+			/* else if(dojo.lang.isObject(kwArgs.handler)){
+				if(isFunction(kwArgs.handler[fn])){
+					kwArgs[fn] = kwArgs.handler[fn]||kwArgs.handler["handle"]||function(){};
+				}
+			}*/
+		}
+		dojo.lang.mixin(this, kwArgs);
+	}
+
+});
+
+dojo.io.Error = function(msg, type, num){
+	this.message = msg;
+	this.type =  type || "unknown"; // must be one of "io", "parse", "unknown"
+	this.number = num || 0; // per-substrate error number, not normalized
+}
+
+dojo.io.transports.addTransport = function(name){
+	this.push(name);
+	// FIXME: do we need to handle things that aren't direct children of the
+	// dojo.io namespace? (say, dojo.io.foo.fooTransport?)
+	this[name] = dojo.io[name];
+}
+
+// binding interface, the various implementations register their capabilities
+// and the bind() method dispatches
+dojo.io.bind = function(request){
+	// if the request asks for a particular implementation, use it
+	if(!(request instanceof dojo.io.Request)){
+		try{
+			request = new dojo.io.Request(request);
+		}catch(e){ dojo.debug(e); }
+	}
+	var tsName = "";
+	if(request["transport"]){
+		tsName = request["transport"];
+		// FIXME: it would be good to call the error handler, although we'd
+		// need to use setTimeout or similar to accomplish this and we can't
+		// garuntee that this facility is available.
+		if(!this[tsName]){ return request; }
+	}else{
+		// otherwise we do our best to auto-detect what available transports
+		// will handle 
+		for(var x=0; x<dojo.io.transports.length; x++){
+			var tmp = dojo.io.transports[x];
+			if((this[tmp])&&(this[tmp].canHandle(request))){
+				tsName = tmp;
+			}
+		}
+		if(tsName == ""){ return request; }
+	}
+	this[tsName].bind(request);
+	request.bindSuccess = true;
+	return request;
+}
+
+dojo.io.queueBind = function(request){
+	if(!(request instanceof dojo.io.Request)){
+		try{
+			request = new dojo.io.Request(request);
+		}catch(e){ dojo.debug(e); }
+	}
+
+	// make sure we get called if/when we get a response
+	var oldLoad = request.load;
+	request.load = function(){
+		dojo.io._queueBindInFlight = false;
+		var ret = oldLoad.apply(this, arguments);
+		dojo.io._dispatchNextQueueBind();
+		return ret;
+	}
+
+	var oldErr = request.error;
+	request.error = function(){
+		dojo.io._queueBindInFlight = false;
+		var ret = oldErr.apply(this, arguments);
+		dojo.io._dispatchNextQueueBind();
+		return ret;
+	}
+
+	dojo.io._bindQueue.push(request);
+	dojo.io._dispatchNextQueueBind();
+	return request;
+}
+
+dojo.io._dispatchNextQueueBind = function(){
+	if(!dojo.io._queueBindInFlight){
+		dojo.io._queueBindInFlight = true;
+		if(dojo.io._bindQueue.length > 0){
+			dojo.io.bind(dojo.io._bindQueue.shift());
+		}else{
+			dojo.io._queueBindInFlight = false;
+		}
+	}
+}
+dojo.io._bindQueue = [];
+dojo.io._queueBindInFlight = false;
+
+dojo.io.argsFromMap = function(map, encoding, last){
+	var enc = /utf/i.test(encoding||"") ? encodeURIComponent : dojo.string.encodeAscii;
+	var mapped = [];
+	var control = new Object();
+	for(var name in map){
+		var domap = function(elt){
+			var val = enc(name)+"="+enc(elt);
+			mapped[(last == name) ? "push" : "unshift"](val);
+		}
+		if(!control[name]){
+			var value = map[name];
+			// FIXME: should be isArrayLike?
+			if (dojo.lang.isArray(value)){
+				dojo.lang.forEach(value, domap);
+			}else{
+				domap(value);
+			}
+		}
+	}
+	return mapped.join("&");
+}
+
+dojo.io.setIFrameSrc = function(iframe, src, replace){
+	try{
+		var r = dojo.render.html;
+		// dojo.debug(iframe);
+		if(!replace){
+			if(r.safari){
+				iframe.location = src;
+			}else{
+				frames[iframe.name].location = src;
+			}
+		}else{
+			// Fun with DOM 0 incompatibilities!
+			var idoc;
+			if(r.ie){
+				idoc = iframe.contentWindow.document;
+			}else if(r.safari){
+				idoc = iframe.document;
+			}else{ //  if(r.moz){
+				idoc = iframe.contentWindow;
+			}
+
+			//For Safari (at least 2.0.3) and Opera, if the iframe
+			//has just been created but it doesn't have content
+			//yet, then iframe.document may be null. In that case,
+			//use iframe.location and return.
+			if(!idoc){
+				iframe.location = src;
+				return;
+			}else{
+				idoc.location.replace(src);
+			}
+		}
+	}catch(e){ 
+		dojo.debug(e); 
+		dojo.debug("setIFrameSrc: "+e); 
+	}
+}
+
+/*
+dojo.io.sampleTranport = new function(){
+	this.canHandle = function(kwArgs){
+		// canHandle just tells dojo.io.bind() if this is a good transport to
+		// use for the particular type of request.
+		if(	
+			(
+				(kwArgs["mimetype"] == "text/plain") ||
+				(kwArgs["mimetype"] == "text/html") ||
+				(kwArgs["mimetype"] == "text/javascript")
+			)&&(
+				(kwArgs["method"] == "get") ||
+				( (kwArgs["method"] == "post") && (!kwArgs["formNode"]) )
+			)
+		){
+			return true;
+		}
+
+		return false;
+	}
+
+	this.bind = function(kwArgs){
+		var hdlrObj = {};
+
+		// set up a handler object
+		for(var x=0; x<dojo.io.hdlrFuncNames.length; x++){
+			var fn = dojo.io.hdlrFuncNames[x];
+			if(typeof kwArgs.handler == "object"){
+				if(typeof kwArgs.handler[fn] == "function"){
+					hdlrObj[fn] = kwArgs.handler[fn]||kwArgs.handler["handle"];
+				}
+			}else if(typeof kwArgs[fn] == "function"){
+				hdlrObj[fn] = kwArgs[fn];
+			}else{
+				hdlrObj[fn] = kwArgs["handle"]||function(){};
+			}
+		}
+
+		// build a handler function that calls back to the handler obj
+		var hdlrFunc = function(evt){
+			if(evt.type == "onload"){
+				hdlrObj.load("load", evt.data, evt);
+			}else if(evt.type == "onerr"){
+				var errObj = new dojo.io.Error("sampleTransport Error: "+evt.msg);
+				hdlrObj.error("error", errObj);
+			}
+		}
+
+		// the sample transport would attach the hdlrFunc() when sending the
+		// request down the pipe at this point
+		var tgtURL = kwArgs.url+"?"+dojo.io.argsFromMap(kwArgs.content);
+		// sampleTransport.sendRequest(tgtURL, hdlrFunc);
+	}
+
+	dojo.io.transports.addTransport("sampleTranport");
+}
+*/


Property changes on: trunk/PhotoOp/root/static/js/src/io.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/json.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/json.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/json.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,135 @@
+/*
+	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.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;
+		}
+	},
+
+	evalJSON: function (json) {
+		dojo.deprecated("dojo.json.evalJSON", "use dojo.json.evalJson", "0.4");
+		return this.evalJson(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(",") + "}";
+	}
+};


Property changes on: trunk/PhotoOp/root/static/js/src/json.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/lang/Lang.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/lang/Lang.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/lang/Lang.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -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");


Property changes on: trunk/PhotoOp/root/static/js/src/lang/Lang.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/lang/__package__.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/lang/__package__.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/lang/__package__.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,24 @@
+/*
+	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.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.*");


Property changes on: trunk/PhotoOp/root/static/js/src/lang/__package__.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/lang/array.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/lang/array.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/lang/array.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,172 @@
+/*
+	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.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<arr.length;++i){
+			outArr.push(unary_func.call(obj, arr[i]));
+		}
+	}
+	if(isString) {
+		return outArr.join("");
+	} else {
+		return outArr;
+	}
+}
+
+// http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:forEach
+dojo.lang.forEach = function(anArray /* Array */, callback /* Function */, thisObject /* Object */){
+	if(dojo.lang.isString(anArray)){ 
+		anArray = anArray.split(""); 
+	}
+	if(Array.forEach){
+		Array.forEach(anArray, callback, thisObject);
+	}else{
+		// FIXME: there are several ways of handilng thisObject. Is dj_global always the default context?
+		if(!thisObject){
+			thisObject=dj_global;
+		}
+		for(var i=0,l=anArray.length; i<l; i++){ 
+			callback.call(thisObject, anArray[i], i, anArray);
+		}
+	}
+}
+
+dojo.lang._everyOrSome = function(every, arr, callback, thisObject){
+	if(dojo.lang.isString(arr)){ 
+		arr = arr.split(""); 
+	}
+	if(Array.every){
+		return Array[ (every) ? "every" : "some" ](arr, callback, thisObject);
+	}else{
+		if(!thisObject){
+			thisObject = dj_global;
+		}
+		for(var i=0,l=arr.length; i<l; i++){
+			var result = callback.call(thisObject, arr[i], i, arr);
+			if((every)&&(!result)){
+				return false;
+			}else if((!every)&&(result)){
+				return true;
+			}
+		}
+		return (every) ? true : false;
+	}
+}
+
+dojo.lang.every = function(arr, callback, thisObject){
+	return this._everyOrSome(true, arr, callback, thisObject);
+}
+
+dojo.lang.some = function(arr, callback, thisObject){
+	return this._everyOrSome(false, arr, callback, thisObject);
+}
+
+dojo.lang.filter = function(arr, callback, thisObject) {
+	var isString = dojo.lang.isString(arr);
+	if(isString) { arr = arr.split(""); }
+	if(Array.filter) {
+		var outArr = Array.filter(arr, callback, thisObject);
+	} else {
+		if(!thisObject) {
+			if(arguments.length >= 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;
+}


Property changes on: trunk/PhotoOp/root/static/js/src/lang/array.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/lang/assert.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/lang/assert.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/lang/assert.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,121 @@
+/*
+	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.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:
+ * <pre>
+ *   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);
+ * </pre>
+ *
+ * @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	message	Optional. A string describing the assertion.
+ * @throws	Throws an Error if 'value' is not of type 'type'.
+ */
+dojo.lang.assertType = function(value, type, message){
+	if(!dojo.lang.isOfType(value, type)){
+		if(!message){
+			if(!dojo.lang.assertType._errorMessage){
+				dojo.lang.assertType._errorMessage = "Type mismatch: dojo.lang.assertType() failed.";
+			}
+			message = dojo.lang.assertType._errorMessage;
+		}
+		dojo.lang.assert(false, message);
+	}
+}
+
+/**
+ * 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:
+ * <pre>
+ *   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});
+ * </pre>
+ *
+ * @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);
+			}
+		}
+	}
+}


Property changes on: trunk/PhotoOp/root/static/js/src/lang/assert.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/lang/common.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/lang/common.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/lang/common.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,190 @@
+/*
+	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.lang.common");
+dojo.require("dojo.lang");
+
+/*
+ * Adds the given properties/methods to the specified object
+ */
+dojo.lang._mixin = function(obj, props){
+	var tobj = {};
+	for(var x in props){
+		// the "tobj" condition avoid copying properties in "props"
+		// inherited from Object.prototype.  For example, if obj has a custom
+		// toString() method, don't overwrite it with the toString() method
+		// that props inherited from Object.protoype
+		if(typeof tobj[x] == "undefined" || tobj[x] != props[x]) {
+			obj[x] = props[x];
+		}
+	}
+	// IE doesn't recognize custom toStrings in for..in
+	if(dojo.render.html.ie && dojo.lang.isFunction(props["toString"]) && props["toString"] != obj["toString"]) {
+		obj.toString = props.toString;
+	}
+	return obj;
+}
+
+/*
+ * Adds the properties/methods of argument Objects to obj
+ */
+dojo.lang.mixin = function(obj, props /*, props, ..., props */){
+	for(var i=1, l=arguments.length; i<l; i++){
+		dojo.lang._mixin(obj, arguments[i]);
+	}
+	return obj;
+}
+
+/*
+ * Adds the properties/methods of argument Objects to ctor's prototype
+ */
+dojo.lang.extend = function(ctor /*function*/, props /*, props, ..., props */){
+	for(var i=1, l=arguments.length; i<l; i++){
+		dojo.lang._mixin(ctor.prototype, arguments[i]);
+	}
+	return ctor;
+}
+
+/**
+ * See if val is in arr. Call signatures:
+ *  find(array, value, identity) // recommended
+ *  find(value, array, identity)
+**/
+dojo.lang.find = function(	/*Array*/	arr, 
+							/*Object*/	val,
+							/*boolean*/	identity,
+							/*boolean*/	findLast){
+	// support both (arr, val) and (val, arr)
+	if(!dojo.lang.isArrayLike(arr) && dojo.lang.isArrayLike(val)) {
+		var a = arr;
+		arr = val;
+		val = a;
+	}
+	var isString = dojo.lang.isString(arr);
+	if(isString) { arr = arr.split(""); }
+
+	if(findLast) {
+		var step = -1;
+		var i = arr.length - 1;
+		var end = -1;
+	} else {
+		var step = 1;
+		var i = 0;
+		var end = arr.length;
+	}
+	if(identity){
+		while(i != end) {
+			if(arr[i] === val){ return i; }
+			i += step;
+		}
+	}else{
+		while(i != end) {
+			if(arr[i] == val){ return i; }
+			i += step;
+		}
+	}
+	return -1;
+}
+
+dojo.lang.indexOf = dojo.lang.find;
+
+dojo.lang.findLast = function(/*Array*/ arr, /*Object*/ val, /*boolean*/ identity){
+	return dojo.lang.find(arr, val, identity, true);
+}
+
+dojo.lang.lastIndexOf = dojo.lang.findLast;
+
+dojo.lang.inArray = function(arr /*Array*/, val /*Object*/){
+	return dojo.lang.find(arr, val) > -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(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; } // keeps out built-in ctors (Number, String, ...) which have length properties
+	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");
+}
+
+/**
+ * The following is***() functions are somewhat "unsafe". Fortunately,
+ * there are workarounds the the language provides and are mentioned
+ * in the WARNING messages.
+ *
+ * WARNING: In most cases, isNaN(wh) 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 (arr["1"] is the same as
+ * arr[1]) and isNaN will return false for both values ("1" and 1). Should you
+ * use isNumber("1"), that 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(wh) when possible
+ */
+dojo.lang.isNumber = function(wh){
+	return (wh instanceof Number || typeof wh == "number");
+}
+
+/**
+ * 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
+ *
+ * FIXME: Should isUndefined go away since it is error prone?
+ */
+dojo.lang.isUndefined = function(wh){
+	return ((wh == undefined)&&(typeof wh == "undefined"));
+}
+
+// end Crockford functions


Property changes on: trunk/PhotoOp/root/static/js/src/lang/common.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/lang/declare.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/lang/declare.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/lang/declare.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,156 @@
+/*
+	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.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(<method name>[, <argument array>]);
+ * 
+ * - 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<l; i++){
+		dojo.lang.extend(ctor, mixins[i].prototype);
+	}
+	ctor.prototype.initializer = null;
+	ctor.prototype.declaredClass = className;
+	if(dojo.lang.isArray(props)){
+		dojo.lang.extend.apply(dojo.lang, [ctor].concat(props));
+	}else{
+		dojo.lang.extend(ctor, (props)||{});
+	}
+	dojo.lang.extend(ctor, dojo.lang.declare.base);
+	ctor.prototype.constructor = ctor;
+	ctor.prototype.initializer=(ctor.prototype.initializer)||(init)||(function(){});
+	dojo.lang.setObjPathValue(className, ctor, null, true);
+}
+
+dojo.lang.declare._makeConstructor = function() {
+	return function(){ 
+		// get the generational context (which object [or prototype] should be constructed)
+		var self = this._getPropContext();
+		var s = self.constructor.superclass;
+		if((s)&&(s.constructor)){
+			if(s.constructor==arguments.callee){
+				// if this constructor is invoked directly (my.ancestor.call(this))
+				this.inherited("constructor", arguments);
+			}else{
+				this._inherited(s, "constructor", arguments);
+			}
+		}
+		var m = (self.constructor.mixins)||([]);
+		for(var i=0,l=m.length; i<l; i++) {
+			(((m[i].prototype)&&(m[i].prototype.initializer))||(m[i])).apply(this, arguments);
+		}
+		if((!this.prototyping)&&(self.initializer)){
+			self.initializer.apply(this, arguments);
+		}
+	}
+}
+
+dojo.lang.declare.base = {
+	_getPropContext: function() { return (this.___proto||this); },
+	// caches ptype context and calls method on it
+	_inherited: function(ptype, method, args){
+		var stack = this.___proto;
+		this.___proto = ptype;
+		var result = ptype[method].apply(this,(args||[]));
+		this.___proto = stack;
+		return result;
+	},
+	// invokes ctor.prototype.method, with args, in our context 
+	inheritedFrom: function(ctor, prop, args){
+		var p = ((ctor)&&(ctor.prototype)&&(ctor.prototype[prop]));
+		return (dojo.lang.isFunction(p) ? p.apply(this, (args||[])) : p);
+	},
+	// searches backward thru prototype chain to find nearest ancestral instance of prop
+	inherited: function(prop, args){
+		var p = this._getPropContext();
+		do{
+			if((!p.constructor)||(!p.constructor.superclass)){return;}
+			p = p.constructor.superclass;
+		}while(!(prop in p));
+		return (dojo.lang.isFunction(p[prop]) ? this._inherited(p, prop, args) : p[prop]);
+	}
+}
+
+dojo.declare = dojo.lang.declare;
\ No newline at end of file


Property changes on: trunk/PhotoOp/root/static/js/src/lang/declare.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/lang/extras.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/lang/extras.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/lang/extras.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,108 @@
+/*
+	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.lang.extras");
+
+dojo.require("dojo.lang.common");
+
+/**
+ * Sets a timeout in milliseconds to execute a function in a given context
+ * with optional arguments.
+ *
+ * setTimeout (Object context, function func, number delay[, arg1[, ...]]);
+ * setTimeout (function func, number delay[, arg1[, ...]]);
+ */
+dojo.lang.setTimeout = function(func, delay){
+	var context = window, argsStart = 2;
+	if(!dojo.lang.isFunction(func)){
+		context = func;
+		func = delay;
+		delay = arguments[2];
+		argsStart++;
+	}
+
+	if(dojo.lang.isString(func)){
+		func = context[func];
+	}
+	
+	var args = [];
+	for (var i = argsStart; i < arguments.length; i++) {
+		args.push(arguments[i]);
+	}
+	return setTimeout(function () { func.apply(context, args); }, delay);
+}
+
+dojo.lang.getNameInObj = function(ns, item){
+	if(!ns){ ns = dj_global; }
+
+	for(var x in ns){
+		if(ns[x] === item){
+			return new String(x);
+		}
+	}
+	return null;
+}
+
+dojo.lang.shallowCopy = function(obj) {
+	var ret = {}, key;
+	for(key in obj) {
+		if(dojo.lang.isUndefined(ret[key])) {
+			ret[key] = obj[key];
+		}
+	}
+	return ret;
+}
+
+/**
+ * Return the first argument that isn't undefined
+ */
+dojo.lang.firstValued = function(/* ... */) {
+	for(var i = 0; i < arguments.length; i++) {
+		if(typeof arguments[i] != "undefined") {
+			return arguments[i];
+		}
+	}
+	return undefined;
+}
+
+/**
+ * Get a value from a reference specified as a string descriptor,
+ * (e.g. "A.B") in the given context.
+ * 
+ * getObjPathValue(String objpath [, Object context, Boolean create])
+ *
+ * If context is not specified, dj_global is used
+ * If create is true, undefined objects in the path are created.
+ */
+dojo.lang.getObjPathValue = function(objpath, context, create){
+	with(dojo.parseObjPath(objpath, context, create)){
+		return dojo.evalProp(prop, obj, create);
+	}
+}
+
+/**
+ * Set a value on a reference specified as a string descriptor. 
+ * (e.g. "A.B") in the given context.
+ * 
+ * setObjPathValue(String objpath, value [, Object context, Boolean create])
+ *
+ * If context is not specified, dj_global is used
+ * If create is true, undefined objects in the path are created.
+ */
+dojo.lang.setObjPathValue = function(objpath, value, context, create){
+	if(arguments.length < 4){
+		create = true;
+	}
+	with(dojo.parseObjPath(objpath, context, create)){
+		if(obj && (create || (prop in obj))){
+			obj[prop] = value;
+		}
+	}
+}


Property changes on: trunk/PhotoOp/root/static/js/src/lang/extras.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/lang/func.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/lang/func.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/lang/func.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,150 @@
+/*
+	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.lang.func");
+
+dojo.require("dojo.lang.common");
+
+/**
+ * Runs a function in a given scope (thisObject), can
+ * also be used to preserve scope.
+ *
+ * hitch(foo, "bar"); // runs foo.bar() in the scope of foo
+ * hitch(foo, myFunction); // runs myFunction in the scope of foo
+ */
+dojo.lang.hitch = function(thisObject, method) {
+	if(dojo.lang.isString(method)) {
+		var fcn = thisObject[method];
+	} else {
+		var fcn = method;
+	}
+
+	return function() {
+		return fcn.apply(thisObject, arguments);
+	}
+}
+
+dojo.lang.anonCtr = 0;
+dojo.lang.anon = {};
+dojo.lang.nameAnonFunc = function(anonFuncPtr, namespaceObj, searchForNames){
+	var nso = (namespaceObj || dojo.lang.anon);
+	if( (searchForNames) ||
+		((dj_global["djConfig"])&&(djConfig["slowAnonFuncLookups"] == true)) ){
+		for(var x in nso){
+			if(nso[x] === anonFuncPtr){
+				return x;
+			}
+		}
+	}
+	var ret = "__"+dojo.lang.anonCtr++;
+	while(typeof nso[ret] != "undefined"){
+		ret = "__"+dojo.lang.anonCtr++;
+	}
+	nso[ret] = anonFuncPtr;
+	return ret;
+}
+
+dojo.lang.forward = function(funcName){
+	// Returns a function that forwards a method call to this.func(...)
+	return function(){
+		return this[funcName].apply(this, arguments);
+	};
+}
+
+dojo.lang.curry = function(ns, func /* args ... */){
+	var outerArgs = [];
+	ns = ns||dj_global;
+	if(dojo.lang.isString(func)){
+		func = ns[func];
+	}
+	for(var x=2; x<arguments.length; x++){
+		outerArgs.push(arguments[x]);
+	}
+	// since the event system replaces the original function with a new
+	// join-point runner with an arity of 0, we check to see if it's left us
+	// any clues about the original arity in lieu of the function's actual
+	// length property
+	var ecount = (func["__preJoinArity"]||func.length) - outerArgs.length;
+	// borrowed from svend tofte
+	function gather(nextArgs, innerArgs, expected){
+		var texpected = expected;
+		var totalArgs = innerArgs.slice(0); // copy
+		for(var x=0; x<nextArgs.length; x++){
+			totalArgs.push(nextArgs[x]);
+		}
+		// check the list of provided nextArgs to see if it, plus the
+		// number of innerArgs already supplied, meets the total
+		// expected.
+		expected = expected-nextArgs.length;
+		if(expected<=0){
+			var res = func.apply(ns, totalArgs);
+			expected = texpected;
+			return res;
+		}else{
+			return function(){
+				return gather(arguments,// check to see if we've been run
+										// with enough args
+							totalArgs,	// a copy
+							expected);	// how many more do we need to run?;
+			}
+		}
+	}
+	return gather([], outerArgs, ecount);
+}
+
+dojo.lang.curryArguments = function(ns, func, args, offset){
+	var targs = [];
+	var x = offset||0;
+	for(x=offset; x<args.length; x++){
+		targs.push(args[x]); // ensure that it's an arr
+	}
+	return dojo.lang.curry.apply(dojo.lang, [ns, func].concat(targs));
+}
+
+dojo.lang.tryThese = function(){
+	for(var x=0; x<arguments.length; x++){
+		try{
+			if(typeof arguments[x] == "function"){
+				var ret = (arguments[x]());
+				if(ret){
+					return ret;
+				}
+			}
+		}catch(e){
+			dojo.debug(e);
+		}
+	}
+}
+
+dojo.lang.delayThese = function(farr, cb, delay, onend){
+	/**
+	 * alternate: (array funcArray, function callback, function onend)
+	 * alternate: (array funcArray, function callback)
+	 * alternate: (array funcArray)
+	 */
+	if(!farr.length){ 
+		if(typeof onend == "function"){
+			onend();
+		}
+		return;
+	}
+	if((typeof delay == "undefined")&&(typeof cb == "number")){
+		delay = cb;
+		cb = function(){};
+	}else if(!cb){
+		cb = function(){};
+		if(!delay){ delay = 0; }
+	}
+	setTimeout(function(){
+		(farr.shift())();
+		cb();
+		dojo.lang.delayThese(farr, cb, delay, onend);
+	}, delay);
+}


Property changes on: trunk/PhotoOp/root/static/js/src/lang/func.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/lang/repr.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/lang/repr.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/lang/repr.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,90 @@
+/*
+	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.lang.repr");
+
+dojo.require("dojo.lang.common");
+dojo.require("dojo.AdapterRegistry");
+dojo.require("dojo.string.extras");
+
+dojo.lang.reprRegistry = new dojo.AdapterRegistry();
+dojo.lang.registerRepr = function(name, check, wrap, /*optional*/ override){
+        /***
+			Register a repr function.  repr functions should take
+			one argument and return a string representation of it
+			suitable for developers, primarily used when debugging.
+
+			If override is given, it is used as the highest priority
+			repr, otherwise it will be used as the lowest.
+        ***/
+        dojo.lang.reprRegistry.register(name, check, wrap, override);
+    };
+
+dojo.lang.repr = function(obj){
+	/***
+		Return a "programmer representation" for an object
+	***/
+	if(typeof(obj) == "undefined"){
+		return "undefined";
+	}else if(obj === null){
+		return "null";
+	}
+
+	try{
+		if(typeof(obj["__repr__"]) == 'function'){
+			return obj["__repr__"]();
+		}else if((typeof(obj["repr"]) == 'function')&&(obj.repr != arguments.callee)){
+			return obj["repr"]();
+		}
+		return dojo.lang.reprRegistry.match(obj);
+	}catch(e){
+		if(typeof(obj.NAME) == 'string' && (
+				obj.toString == Function.prototype.toString ||
+				obj.toString == Object.prototype.toString
+			)){
+			return o.NAME;
+		}
+	}
+
+	if(typeof(obj) == "function"){
+		obj = (obj + "").replace(/^\s+/, "");
+		var idx = obj.indexOf("{");
+		if(idx != -1){
+			obj = obj.substr(0, idx) + "{...}";
+		}
+	}
+	return obj + "";
+}
+
+dojo.lang.reprArrayLike = function(arr){
+	try{
+		var na = dojo.lang.map(arr, dojo.lang.repr);
+		return "[" + na.join(", ") + "]";
+	}catch(e){ }
+};
+
+dojo.lang.reprString = function(str){ 
+	dojo.deprecated("dojo.lang.reprNumber", "use `String(num)` instead", "0.4");
+	return dojo.string.escapeString(str);
+};
+
+dojo.lang.reprNumber = function(num){
+	dojo.deprecated("dojo.lang.reprNumber", "use `String(num)` instead", "0.4");
+	return num + "";
+};
+
+(function(){
+	var m = dojo.lang;
+	m.registerRepr("arrayLike", m.isArrayLike, m.reprArrayLike);
+	m.registerRepr("string", m.isString, m.reprString);
+	m.registerRepr("numbers", m.isNumber, m.reprNumber);
+	m.registerRepr("boolean", m.isBoolean, m.reprNumber);
+	// m.registerRepr("numbers", m.typeMatcher("number", "boolean"), m.reprNumber);
+})();


Property changes on: trunk/PhotoOp/root/static/js/src/lang/repr.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/lang/type.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/lang/type.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/lang/type.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,226 @@
+/*
+	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.lang.type");
+
+dojo.require("dojo.lang.common");
+
+dojo.lang.whatAmI = function(wh) {
+	try {
+		if(dojo.lang.isArray(wh)) { return "array"; }
+		if(dojo.lang.isFunction(wh)) { return "function"; }
+		if(dojo.lang.isString(wh)) { return "string"; }
+		if(dojo.lang.isNumber(wh)) { return "number"; }
+		if(dojo.lang.isBoolean(wh)) { return "boolean"; }
+		if(dojo.lang.isAlien(wh)) { return "alien"; }
+		if(dojo.lang.isUndefined(wh)) { return "undefined"; }
+		// FIXME: should this go first?
+		for(var name in dojo.lang.whatAmI.custom) {
+			if(dojo.lang.whatAmI.custom[name](wh)) {
+				return name;
+			}
+		}
+		if(dojo.lang.isObject(wh)) { return "object"; }
+	} catch(E) {}
+	return "unknown";
+}
+/*
+ * dojo.lang.whatAmI.custom[typeName] = someFunction
+ * will return typeName is someFunction(wh) returns true
+ */
+dojo.lang.whatAmI.custom = {};
+
+/**
+ * Returns true for values that commonly represent numbers.
+ *
+ * Examples:
+ * <pre>
+ *   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
+ * </pre>
+ */
+dojo.lang.isNumeric = function(wh){
+	return (!isNaN(wh) && isFinite(wh) && (wh != null) &&
+			!dojo.lang.isBoolean(wh) && !dojo.lang.isArray(wh));
+}
+
+/**
+ * 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(wh){
+	return (dojo.lang.isArray(wh)		|| 
+			dojo.lang.isFunction(wh)	|| 
+			dojo.lang.isString(wh)		|| 
+			dojo.lang.isNumber(wh)		|| 
+			dojo.lang.isBoolean(wh)		|| 
+			(wh == null)				|| 
+			(wh instanceof Error)		|| 
+			(typeof wh == "error") );
+}
+
+/**
+ * Returns true for any object where the value of the 
+ * property 'constructor' is 'Object'.  
+ * 
+ * Examples:
+ * <pre>
+ *   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
+ * </pre>
+ */
+dojo.lang.isPureObject = function(wh){
+	return ((wh != null) && dojo.lang.isObject(wh) && wh.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:
+ * <pre>
+ *   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(xxxxx, 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(xxxxx, "undefined");               // returns true
+ * </pre>
+ *
+ * @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) {
+	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":
+				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<parts.length&&obj); 
+	return (obj!=dj_global)?obj:null;	//	Object
+}
+
+dojo.lang.doesObjectExist=function(/* String */ str){
+	//	summary
+	//	Check to see if object [str] exists, based on the passed string.
+	var parts=str.split("."), i=0, obj=dj_global; 
+	do{ 
+		obj=obj[parts[i++]]; 
+	}while(i<parts.length&&obj); 
+	return (obj&&obj!=dj_global);	//	boolean
+}


Property changes on: trunk/PhotoOp/root/static/js/src/lang/type.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/lang.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/lang.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/lang.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,14 @@
+/*
+	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.lang");
+dojo.provide("dojo.lang.Lang");
+
+dojo.require("dojo.lang.common");


Property changes on: trunk/PhotoOp/root/static/js/src/lang.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/lfx/Animation.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/lfx/Animation.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/lfx/Animation.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,476 @@
+/*
+	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.lfx.Animation");
+dojo.provide("dojo.lfx.Line");
+
+dojo.require("dojo.lang.func");
+
+/*
+	Animation package based on Dan Pupius' work: http://pupius.co.uk/js/Toolkit.Drawing.js
+*/
+dojo.lfx.Line = function(start, end){
+	this.start = start;
+	this.end = end;
+	if(dojo.lang.isArray(start)){
+		var diff = [];
+		dojo.lang.forEach(this.start, function(s,i){
+			diff[i] = this.end[i] - s;
+		}, this);
+		
+		this.getValue = function(/*float*/ n){
+			var res = [];
+			dojo.lang.forEach(this.start, function(s, i){
+				res[i] = (diff[i] * n) + s;
+			}, this);
+			return res;
+		}
+	}else{
+		var diff = end - start;
+			
+		this.getValue = function(/*float*/ n){
+			//	summary: returns the point on the line
+			//	n: a floating point number greater than 0 and less than 1
+			return (diff * n) + this.start;
+		}
+	}
+}
+
+dojo.lfx.easeIn = function(n){
+	//	summary: returns the point on an easing curve
+	//	n: a floating point number greater than 0 and less than 1
+	return Math.pow(n, 3);
+}
+
+dojo.lfx.easeOut = function(n){
+	//	summary: returns the point on the line
+	//	n: a floating point number greater than 0 and less than 1
+	return ( 1 - Math.pow(1 - n, 3) );
+}
+
+dojo.lfx.easeInOut = function(n){
+	//	summary: returns the point on the line
+	//	n: a floating point number greater than 0 and less than 1
+	return ( (3 * Math.pow(n, 2)) - (2 * Math.pow(n, 3)) );
+}
+
+dojo.lfx.IAnimation = function(){}
+dojo.lang.extend(dojo.lfx.IAnimation, {
+	// public properties
+	curve: null,
+	duration: 1000,
+	easing: null,
+	repeatCount: 0,
+	rate: 25,
+	
+	// events
+	handler: null,
+	beforeBegin: null,
+	onBegin: null,
+	onAnimate: null,
+	onEnd: null,
+	onPlay: null,
+	onPause: null,
+	onStop: null,
+	
+	// public methods
+	play: null,
+	pause: null,
+	stop: null,
+	
+	fire: function(evt, args){
+		if(this[evt]){
+			this[evt].apply(this, (args||[]));
+		}
+	},
+	
+	// private properties
+	_active: false,
+	_paused: false
+});
+
+dojo.lfx.Animation = function(/*Object*/ handlers, /*int*/ duration, /*Array*/ curve, /*function*/ easing, /*int*/ repeatCount, /*int*/ rate){
+	//	summary
+	//		a generic animation object that fires callbacks into it's handlers
+	//		object at various states
+	//	handlers
+	//		object { 
+	//			handler: function(){}, 
+	//			onstart: function(){}, 
+	//			onstop: function(){}, 
+	//			onanimate: function(){}
+	//		}
+	dojo.lfx.IAnimation.call(this);
+	if(dojo.lang.isNumber(handlers)||(!handlers && duration.getValue)){
+		// no handlers argument:
+		rate = repeatCount;
+		repeatCount = easing;
+		easing = curve;
+		curve = duration;
+		duration = handlers;
+		handlers = null;
+	}else if(handlers.getValue||dojo.lang.isArray(handlers)){
+		// no handlers or duration:
+		rate = easing;
+		repeatCount = curve;
+		easing = duration;
+		curve = handlers;
+		duration = null;
+		handlers = null;
+	}
+	if(dojo.lang.isArray(curve)){
+		this.curve = new dojo.lfx.Line(curve[0], curve[1]);
+	}else{
+		this.curve = curve;
+	}
+	if(duration != null && duration > 0){ this.duration = duration; }
+	if(repeatCount){ this.repeatCount = repeatCount; }
+	if(rate){ this.rate = rate; }
+	if(handlers){
+		this.handler = handlers.handler;
+		this.beforeBegin = handlers.beforeBegin;
+		this.onBegin = handlers.onBegin;
+		this.onEnd = handlers.onEnd;
+		this.onPlay = handlers.onPlay;
+		this.onPause = handlers.onPause;
+		this.onStop = handlers.onStop;
+		this.onAnimate = handlers.onAnimate;
+	}
+	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(); }
+	},
+
+	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";
+		}
+	},
+
+	// "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];
+	}
+	
+	var _this = this;
+	dojo.lang.forEach(anims, function(anim){
+		_this._anims.push(anim);
+		var oldOnEnd = (anim["onEnd"]) ? dojo.lang.hitch(anim, "onEnd") : function(){};
+		anim.onEnd = function(){ oldOnEnd(); _this._onAnimsEnded(); };
+	});
+}
+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);
+		var oldOnEnd = (anim["onEnd"]) ? dojo.lang.hitch(anim, "onEnd") : function(){};
+		if(i < anims_arr.length - 1){
+			anim.onEnd = function(){ oldOnEnd(); _this._playNext(); };
+		}else{
+			anim.onEnd = function(){ oldOnEnd(); _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];
+	}
+	return new dojo.lfx.Combine(anims);
+}
+
+dojo.lfx.chain = function(){
+	var anims = arguments;
+	if(dojo.lang.isArray(arguments[0])){
+		anims = arguments[0];
+	}
+	return new dojo.lfx.Chain(anims);
+}


Property changes on: trunk/PhotoOp/root/static/js/src/lfx/Animation.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/lfx/__package__.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/lfx/__package__.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/lfx/__package__.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -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.kwCompoundRequire({
+	browser: ["dojo.lfx.html"],
+	dashboard: ["dojo.lfx.html"]
+});
+dojo.provide("dojo.lfx.*");
\ No newline at end of file


Property changes on: trunk/PhotoOp/root/static/js/src/lfx/__package__.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/lfx/extras.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/lfx/extras.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/lfx/extras.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,119 @@
+/*
+	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.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.wipeIn(nodes, duration, easing),
+		dojo.lfx.fadeIn(nodes, duration, easing));
+	
+	if(callback){
+		dojo.event.connect(anim, "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.wipeOut(nodes, duration, easing),
+		dojo.lfx.fadeOut(nodes, duration, easing));
+	
+	if(callback){
+		dojo.event.connect(anim, "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 origWidth = dojo.style.getOuterWidth(node);
+		var origHeight = dojo.style.getOuterHeight(node);
+
+		var actualPct = percentage/100.0;
+		var props = [
+			{	property: "width",
+				start: origWidth,
+				end: origWidth * actualPct
+			},
+			{	property: "height",
+				start: origHeight,
+				end: origHeight * actualPct
+			}];
+		
+		if(scaleContent){
+			var fontSize = dojo.style.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.style.getStyle(node, "position");
+			var originalTop = node.offsetTop;
+			var originalLeft = node.offsetLeft;
+			var endTop = ((origHeight * actualPct) - origHeight)/2;
+			var endLeft = ((origWidth * actualPct) - origWidth)/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){
+			dojo.event.connect(anim, "onEnd", function(){
+				callback(node, anim);
+			});
+		}
+
+		anims.push(anim);
+	});
+	
+	if(nodes.length > 1){ return dojo.lfx.combine(anims); }
+	else{ return anims[0]; }
+}
+
+dojo.lang.mixin(dojo.lfx, dojo.lfx.html);


Property changes on: trunk/PhotoOp/root/static/js/src/lfx/extras.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/lfx/html.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/lfx/html.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/lfx/html.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,535 @@
+/*
+	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.lfx.html");
+dojo.require("dojo.lfx.Animation");
+
+dojo.require("dojo.html");
+
+dojo.lfx.html._byId = function(nodes){
+	if(!nodes){ return []; }
+	if(dojo.lang.isArray(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){
+	nodes = dojo.lfx.html._byId(nodes);
+	
+	if(nodes.length==1){
+		// FIXME: we're only supporting start-value filling when one node is
+		// passed
+		
+		dojo.lang.forEach(propertyMap, function(prop){
+			if(typeof prop["start"] == "undefined"){
+				if(prop.property != "opacity"){
+					prop.start = parseInt(dojo.style.getComputedStyle(nodes[0], prop.property));
+				}else{
+					prop.start = dojo.style.getOpacity(nodes[0]);
+				}
+			}
+		});
+	}
+
+	var coordsAsInts = function(coords){
+		var cints = new Array(coords.length);
+		for(var i = 0; i < coords.length; i++){
+			cints[i] = Math.round(coords[i]);
+		}
+		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.style.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.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.style.toCamelCase(prop.property)] = value;
+			}, this);
+			return ret;
+		}
+	}
+	
+	var anim = new dojo.lfx.Animation({
+		onAnimate: function(propValues){
+			dojo.lang.forEach(nodes, function(node){
+				setStyle(node, propValues);
+			});
+		} }, duration, new propLine(propertyMap), easing);
+	
+	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.style.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.style.getStyle(node, "width") == "auto") ){
+				node.style.width = "auto";
+			}
+		}
+	}
+	if(dojo.lang.isArrayLike(nodes)){
+		dojo.lang.forEach(nodes, makeFade);
+	}else{
+		makeFade(nodes);
+	}
+}
+
+dojo.lfx.html.fadeIn = function(nodes, duration, easing, callback){
+	nodes = dojo.lfx.html._byId(nodes);
+	dojo.lfx.html._makeFadeable(nodes);
+	var anim = dojo.lfx.propertyAnimation(nodes, [
+		{	property: "opacity",
+			start: dojo.style.getOpacity(nodes[0]),
+			end: 1 } ], duration, easing);
+	if(callback){
+		var oldOnEnd = (anim["onEnd"]) ? dojo.lang.hitch(anim, "onEnd") : function(){};
+		anim.onEnd = function(){ oldOnEnd(); callback(nodes, anim); };
+	}
+
+	return anim;
+}
+
+dojo.lfx.html.fadeOut = function(nodes, duration, easing, callback){
+	nodes = dojo.lfx.html._byId(nodes);
+	dojo.lfx.html._makeFadeable(nodes);
+	var anim = dojo.lfx.propertyAnimation(nodes, [
+		{	property: "opacity",
+			start: dojo.style.getOpacity(nodes[0]),
+			end: 0 } ], duration, easing);
+	if(callback){
+		var oldOnEnd = (anim["onEnd"]) ? dojo.lang.hitch(anim, "onEnd") : function(){};
+		anim.onEnd = function(){ oldOnEnd(); callback(nodes, anim); };
+	}
+
+	return anim;
+}
+
+dojo.lfx.html.fadeShow = function(nodes, duration, easing, callback){
+	var anim = dojo.lfx.html.fadeIn(nodes, duration, easing, callback);
+	var oldBb = (anim["beforeBegin"]) ? dojo.lang.hitch(anim, "beforeBegin") : function(){};
+	anim.beforeBegin = function(){ 
+		oldBb();
+		if(dojo.lang.isArrayLike(nodes)){
+			dojo.lang.forEach(nodes, dojo.style.show);
+		}else{
+			dojo.style.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.style.hide);
+		}else{
+			dojo.style.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 overflow = dojo.style.getStyle(node, "overflow");
+		if(overflow == "visible") {
+			node.style.overflow = "hidden";
+		}
+		node.style.height = "0px";
+		dojo.style.show(node);
+		
+		var anim = dojo.lfx.propertyAnimation(node,
+			[{	property: "height",
+				start: 0,
+				end: node.scrollHeight }], duration, easing);
+		
+		var oldOnEnd = (anim["onEnd"]) ? dojo.lang.hitch(anim, "onEnd") : function(){};
+		anim.onEnd = function(){ 
+			oldOnEnd(); 
+			node.style.overflow = overflow;
+			node.style.height = "auto";
+			if(callback){ callback(node, anim); }
+		};
+		anims.push(anim);
+	});
+	
+	if(nodes.length > 1){ return dojo.lfx.combine(anims); }
+	else{ return anims[0]; }
+}
+
+dojo.lfx.html.wipeOut = function(nodes, duration, easing, callback){
+	nodes = dojo.lfx.html._byId(nodes);
+	var anims = [];
+	
+	dojo.lang.forEach(nodes, function(node){
+		var overflow = dojo.style.getStyle(node, "overflow");
+		if(overflow == "visible") {
+			node.style.overflow = "hidden";
+		}
+		dojo.style.show(node);
+
+		var anim = dojo.lfx.propertyAnimation(node,
+			[{	property: "height",
+				start: dojo.style.getContentBoxHeight(node),
+				end: 0 } ], duration, easing);
+		
+		var oldOnEnd = (anim["onEnd"]) ? dojo.lang.hitch(anim, "onEnd") : function(){};
+		anim.onEnd = function(){ 
+			oldOnEnd(); 
+			dojo.style.hide(node);
+			node.style.overflow = overflow;
+			if(callback){ callback(node, anim); }
+		};
+		anims.push(anim);
+	});
+
+	if(nodes.length > 1){ return dojo.lfx.combine(anims); }
+	else { return anims[0]; }
+}
+
+dojo.lfx.html.slideTo = function(nodes, coords, duration, easing, callback){
+	nodes = dojo.lfx.html._byId(nodes);
+	var anims = [];
+
+	dojo.lang.forEach(nodes, function(node){
+		var top = null;
+		var left = null;
+		
+		var init = (function(){
+			var innerNode = node;
+			return function(){
+				top = innerNode.offsetTop;
+				left = innerNode.offsetLeft;
+
+				if (!dojo.style.isPositionAbsolute(innerNode)) {
+					var ret = dojo.style.abs(innerNode, true);
+					dojo.style.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,
+			[{	property: "top",
+				start: top,
+				end: coords[0] },
+			{	property: "left",
+				start: left,
+				end: coords[1] }], duration, easing);
+		
+		var oldBb = (anim["beforeBegin"]) ? dojo.lang.hitch(anim, "beforeBegin") : function(){};
+		anim.beforeBegin = function(){ oldBb(); init(); };
+
+		if(callback){
+			var oldOnEnd = (anim["onEnd"]) ? dojo.lang.hitch(anim, "onEnd") : function(){};
+			anim.onEnd = function(){ oldOnEnd(); callback(nodes, anim); };
+		}
+
+		anims.push(anim);
+	});
+	
+	if(nodes.length > 1){ return dojo.lfx.combine(anims); }
+	else{ return anims[0]; }
+}
+
+dojo.lfx.html.slideBy = function(nodes, coords, duration, easing, callback){
+	nodes = dojo.lfx.html._byId(nodes);
+	var anims = [];
+
+	dojo.lang.forEach(nodes, function(node){
+		var top = null;
+		var left = null;
+		
+		var init = (function(){
+			var innerNode = node;
+			return function(){
+				top = node.offsetTop;
+				left = node.offsetLeft;
+
+				if (!dojo.style.isPositionAbsolute(innerNode)) {
+					var ret = dojo.style.abs(innerNode);
+					dojo.style.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,
+			[{	property: "top",
+				start: top,
+				end: top+coords[0] },
+			{	property: "left",
+				start: left,
+				end: left+coords[1] }], duration, easing);
+
+		var oldBb = (anim["beforeBegin"]) ? dojo.lang.hitch(anim, "beforeBegin") : function(){};
+		anim.beforeBegin = function(){ oldBb(); init(); };
+
+		if(callback){
+			var oldOnEnd = (anim["onEnd"]) ? dojo.lang.hitch(anim, "onEnd") : function(){};
+			anim.onEnd = function(){ oldOnEnd(); callback(nodes, anim); };
+		}
+
+		anims.push(anim);
+	});
+
+	if(nodes.length > 1){ return dojo.lfx.combine(anims); }
+	else{ return anims[0]; }
+}
+
+dojo.lfx.html.explode = function(start, endNode, duration, easing, callback){
+	start = dojo.byId(start);
+	endNode = dojo.byId(endNode);
+	var startCoords = dojo.style.toCoordinateArray(start, true);
+	var outline = document.createElement("div");
+	dojo.html.copyStyle(outline, endNode);
+	with(outline.style){
+		position = "absolute";
+		display = "none";
+	}
+	document.body.appendChild(outline);
+
+	with(endNode.style){
+		visibility = "hidden";
+		display = "block";
+	}
+	var endCoords = dojo.style.toCoordinateArray(endNode, true);
+	with(endNode.style){
+		display = "none";
+		visibility = "visible";
+	}
+
+	var anim = new dojo.lfx.propertyAnimation(outline, [
+		{ property: "height", start: startCoords[3], end: endCoords[3] },
+		{ property: "width", start: startCoords[2], end: endCoords[2] },
+		{ property: "top", start: startCoords[1], end: endCoords[1] },
+		{ property: "left", start: startCoords[0], end: endCoords[0] },
+		{ property: "opacity", start: 0.3, end: 1.0 }
+	], duration, easing);
+	
+	anim.beforeBegin = function(){
+		dojo.style.setDisplay(outline, "block");
+	};
+	anim.onEnd = function(){
+		dojo.style.setDisplay(endNode, "block");
+		outline.parentNode.removeChild(outline);
+	};
+	if(callback){
+		var oldOnEnd = (anim["onEnd"]) ? dojo.lang.hitch(anim, "onEnd") : function(){};
+		anim.onEnd = function(){ oldOnEnd(); callback(endNode, anim); };
+	}
+	return anim;
+}
+
+dojo.lfx.html.implode = function(startNode, end, duration, easing, callback){
+	startNode = dojo.byId(startNode);
+	end = dojo.byId(end);
+	var startCoords = dojo.style.toCoordinateArray(startNode, true);
+	var endCoords = dojo.style.toCoordinateArray(end, true);
+
+	var outline = document.createElement("div");
+	dojo.html.copyStyle(outline, startNode);
+	dojo.style.setOpacity(outline, 0.3);
+	with(outline.style){
+		position = "absolute";
+		display = "none";
+	}
+	document.body.appendChild(outline);
+
+	var anim = new dojo.lfx.propertyAnimation(outline, [
+		{ property: "height", start: startCoords[3], end: endCoords[3] },
+		{ property: "width", start: startCoords[2], end: endCoords[2] },
+		{ property: "top", start: startCoords[1], end: endCoords[1] },
+		{ property: "left", start: startCoords[0], end: endCoords[0] },
+		{ property: "opacity", start: 1.0, end: 0.3 }
+	], duration, easing);
+	
+	anim.beforeBegin = function(){
+		dojo.style.hide(startNode);
+		dojo.style.show(outline);
+	};
+	anim.onEnd = function(){
+		outline.parentNode.removeChild(outline);
+	};
+	if(callback){
+		var oldOnEnd = (anim["onEnd"]) ? dojo.lang.hitch(anim, "onEnd") : function(){};
+		anim.onEnd = function(){ oldOnEnd(); 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.style.getBackgroundColor(node);
+		var bg = dojo.style.getStyle(node, "background-color").toLowerCase();
+		var bgImage = dojo.style.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, [{
+			property: "background-color",
+			start: rgb,
+			end: endRgb
+		}], duration, easing);
+
+		var oldbb = (anim["beforeBegin"]) ? dojo.lang.hitch(anim, "beforeBegin") : function(){};
+		anim.beforeBegin = function(){ 
+			oldbb();
+			if(bgImage){
+				node.style.backgroundImage = "none";
+			}
+			node.style.backgroundColor = "rgb(" + rgb.toRgb().join(",") + ")";
+		};
+
+		var oldOnEnd = (anim["onEnd"]) ? dojo.lang.hitch(anim, "onEnd") : function(){};
+		anim.onEnd = function(){ 
+			oldOnEnd();
+			if(bgImage){
+				node.style.backgroundImage = bgImage;
+			}
+			if(wasTransparent){
+				node.style.backgroundColor = "transparent";
+			}
+			if(callback){
+				callback(node, anim);
+			}
+		};
+
+		anims.push(anim);
+	});
+
+	if(nodes.length > 1){ return dojo.lfx.combine(anims); }
+	else{ return anims[0]; }
+}
+
+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.style.getBackgroundColor(node));
+		var rgb = new dojo.graphics.color.Color(endColor);
+
+		var bgImage = dojo.style.getStyle(node, "background-image");
+		
+		var anim = dojo.lfx.propertyAnimation(node, [{
+			property: "background-color",
+			start: color,
+			end: rgb
+		}], duration, easing);
+
+		var oldbb = (anim["beforeBegin"]) ? dojo.lang.hitch(anim, "beforeBegin") : function(){};
+		anim.beforeBegin = function(){ 
+			oldbb();
+			if(bgImage){
+				node.style.backgroundImage = "none";
+			}
+			node.style.backgroundColor = "rgb(" + color.toRgb().join(",") + ")";
+		};
+
+		var oldOnEnd = (anim["onEnd"]) ? dojo.lang.hitch(anim, "onEnd") : function(){};
+		anim.onEnd = function(){ 
+			oldOnEnd();
+			if(callback){
+				callback(node, anim);
+			}
+		};
+
+		anims.push(anim);
+	});
+
+	if(nodes.length > 1){ return dojo.lfx.combine(anims); }
+	else{ return anims[0]; }
+}
+
+dojo.lang.mixin(dojo.lfx, dojo.lfx.html);


Property changes on: trunk/PhotoOp/root/static/js/src/lfx/html.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/lfx/toggle.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/lfx/toggle.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/lfx/toggle.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,54 @@
+/*
+	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.lfx.toggle");
+dojo.require("dojo.lfx.*");
+
+dojo.lfx.toggle.plain = {
+	show: function(node, duration, easing, callback){
+		dojo.style.show(node);
+		if(dojo.lang.isFunction(callback)){ callback(); }
+	},
+	
+	hide: function(node, duration, easing, callback){
+		dojo.style.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||[0,0,0,0], node, duration, easing, callback).play();
+	},
+
+	hide: function(node, duration, easing, callback, explodeSrc){
+		dojo.lfx.implode(node, explodeSrc||[0,0,0,0], duration, easing, callback).play();
+	}
+}


Property changes on: trunk/PhotoOp/root/static/js/src/lfx/toggle.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/loader.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/loader.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/loader.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,450 @@
+/*
+	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
+*/
+
+/*
+ * 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<mll.length; x++){
+		mll[x]();
+	}
+
+	//Clear listeners so new ones can be added
+	//For other xdomain package loads after the initial load.
+	this.modulesLoadedListeners = [];
+	this.loadNotifying = false;
+
+	dojo.loaded();
+}
+
+dojo.hostenv.unloaded = function(){
+	var mll = this.unloadListeners;
+	while(mll.length){
+		(mll.pop())();
+	}
+	dojo.unloaded();
+}
+
+/*
+Call styles:
+	dojo.addOnLoad(functionPointer)
+	dojo.addOnLoad(object, "functionName")
+*/
+dojo.addOnLoad = function(obj, fcnName) {
+	var dh = dojo.hostenv;
+	if(arguments.length == 1) {
+		dh.modulesLoadedListeners.push(obj);
+	} else if(arguments.length > 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;
+}
+
+/**
+* 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 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
+	var nsyms = modulename.split(".");
+	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){
+	var modref = dojo.evalObjPath((packname.split(".").slice(0, -1)).join('.'));
+	this.loaded_modules_[(new String(packname)).toLowerCase()] = modref;
+
+	var syms = packname.split(/\./);
+	if(syms[syms.length-1]=="*"){
+		syms.pop();
+	}
+	return dojo.evalObjPath(syms.join("."), true);
+}
+
+/**
+ * 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){
+	// check cache
+	/*
+	if(!dj_undef(modulename, this.modules_)){
+		return this.modules_[modulename];
+	}
+	*/
+
+	var lmn = (new String(modulename)).toLowerCase();
+
+	if(this.loaded_modules_[lmn]){
+		return this.loaded_modules_[lmn];
+	}
+
+	// see if symbol is defined anyway
+	var module = dojo.evalObjPath(modulename);
+	if((modulename)&&(typeof module != 'undefined')&&(module)){
+		this.loaded_modules_[lmn] = module;
+		return module;
+	}
+
+	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<result.length; x++){
+		var curr = result[x];
+		if(curr.constructor == Array){
+			dojo.hostenv.loadModule.apply(dojo.hostenv, curr);
+		}else{
+			dojo.hostenv.loadModule(curr);
+		}
+	}
+}
+
+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 args = [];
+		for (var i = 1; i < arguments.length; i++) { args.push(arguments[i]); }
+		dojo.require.apply(dojo, args);
+	}
+}
+
+dojo.requireAfterIf = dojo.requireIf;
+
+dojo.provide = function(){
+	return dojo.hostenv.startPackage.apply(dojo.hostenv, arguments);
+}
+
+dojo.setModulePrefix = function(module, prefix){
+	return dojo.hostenv.setModulePrefix(module, prefix);
+}
+
+// determine if an object supports a given method
+// useful for longer api chains where you have to test each object in the chain
+dojo.exists = function(obj, name){
+	var p = name.split(".");
+	for(var i = 0; i < p.length; i++){
+	if(!(obj[p[i]])) return false;
+		obj = obj[p[i]];
+	}
+	return true;
+}


Property changes on: trunk/PhotoOp/root/static/js/src/loader.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/loader_xd.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/loader_xd.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/loader_xd.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,414 @@
+/*
+	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
+*/
+
+//Cross-domain package loader.
+
+//FIXME: How will xd loading work with debugAtAllCosts? Any bad interactions?
+//FIXME: widgets won't work fully (HTML/CSS) and also because of the requireIf() thing.
+
+dojo.hostenv.resetXd = function(){
+	//This flag indicates where or not we have crossed into xdomain territory. Once any package says
+	//it is cross domain, then the rest of the packages have to be treated as xdomain because we need
+	//to evaluate packages in order. If there is a xdomain package followed by a xhr package, we can't load
+	//the xhr package until the one before it finishes loading. The text of the xhr package will be converted
+	//to match the format for a xd package and put in the xd load queue.
+	//You can force all packages to be treated as xd by setting the djConfig.forceXDomain.
+	this.isXDomain = djConfig.forceXDomain || false;
+
+	this.xdTimer = 0;
+	this.xdInFlight = {};
+	this.xdOrderedReqs = [];
+	this.xdDepMap = {};
+	this.xdContents = [];
+}
+
+//Call reset immediately to set the state.
+dojo.hostenv.resetXd();
+
+dojo.hostenv.createXdPackage = function(contents){
+	//Find dependencies.
+	var deps = [];
+    var depRegExp = /dojo.(require|requireIf|requireAll|provide|requireAfterIf|requireAfter|kwCompoundRequire|conditionalRequire|hostenv\.conditionalLoadModule|.hostenv\.loadModule|hostenv\.moduleLoaded)\(([\w\W]*?)\)/mg;
+    var match;
+	while((match = depRegExp.exec(contents)) != null){
+		deps.push("\"" + match[1] + "\", " + match[2]);
+	}
+
+	//Create package object and the call to packageLoaded.
+	var output = [];
+	output.push("dojo.hostenv.packageLoaded({\n");
+
+	//Add dependencies
+	if(deps.length > 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};
+			}
+		}
+	}
+}


Property changes on: trunk/PhotoOp/root/static/js/src/loader_xd.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/logging/Logger.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/logging/Logger.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/logging/Logger.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,408 @@
+/*
+	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 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<args.length; x++){
+			ret.push(args[x]);
+		}
+		return ret;
+	},
+
+	setLevel: function(lvl){
+		this.cutOffLevel = parseInt(lvl);
+	},
+
+	isEnabledFor: function(lvl){
+		return parseInt(lvl) >= 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.filters.length; x++){
+			if(this.filters[x]===fltRef){
+				delete this.filters[x];
+				return true;
+			}
+		}
+		return false;
+	},
+
+	removeAllFilters: function(){
+		this.filters = []; // clobber all of them
+	},
+
+	filter: function(rec){
+		for(var x=0; x<this.filters.length; x++){
+			if((this.filters[x]["filter"])&&
+			   (!this.filters[x].filter(rec))||
+			   (rec.level<this.cutOffLevel)){
+				return false;
+			}
+		}
+		return true;
+	},
+
+	addHandler: function(hdlr){
+		this.handlers.push(hdlr);
+		return this.handlers.length-1;
+	},
+
+	handle: function(rec){
+		if((!this.filter(rec))||(rec.level<this.cutOffLevel)){ return false; }
+		for(var x=0; x<this.handlers.length; x++){
+			if(this.handlers[x]["handle"]){
+			   this.handlers[x].handle(rec);
+			}
+		}
+		// FIXME: not sure what to do about records to be propagated that may have
+		// been modified by the handlers or the filters at this logger. Should
+		// parents always have pristine copies? or is passing the modified record
+		// OK?
+		// if((this.propagate)&&(this.parent)){ this.parent.handle(rec); }
+		return true;
+	},
+
+	// the heart and soul of the logging system
+	log: function(lvl, msg){
+		if(	(this.propagate)&&(this.parent)&&
+			(this.parent.rec.level>=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(typeof args == "array"){
+			na = na.concat(args);
+		}else if((typeof args == "object")&&(args["length"])){
+			na = na.concat(this.argsToArr(args));
+			/* for(var x=0; x<args.length; x++){
+				na.push(args[x]);
+			} */
+		}else{
+			na = na.concat(this.argsToArr(arguments).slice(1));
+			/* for(var x=1; x<arguments.length; x++){
+				na.push(arguments[x]);
+			} */
+		}
+		return this.log.apply(this, na);
+	}
+});
+
+void(function(){
+	var ptype = dojo.logging.Logger.prototype;
+	ptype.warn = ptype.warning;
+	ptype.err = ptype.error;
+	ptype.crit = ptype.critical;
+})();
+
+// the Handler class
+dojo.logging.LogHandler = function(level){
+	this.cutOffLevel = (level) ? level : 0;
+	this.formatter = null; // FIXME: default formatter?
+	this.data = [];
+	this.filters = [];
+}
+
+dojo.logging.LogHandler.prototype.setFormatter = function(fmtr){
+	// FIXME: need to vet that it is indeed a formatter object
+	dojo.unimplemented("setFormatter");
+}
+
+dojo.logging.LogHandler.prototype.flush = function(){
+	dojo.unimplemented("flush");
+}
+
+dojo.logging.LogHandler.prototype.close = function(){
+	dojo.unimplemented("close");
+}
+
+dojo.logging.LogHandler.prototype.handleError = function(){
+	dojo.unimplemented("handleError");
+}
+
+dojo.logging.LogHandler.prototype.handle = function(record){
+	// emits the passed record if it passes this object's filters
+	if((this.filter(record))&&(record.level>=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; x<names.length; x++){
+		tgt[names[x]] = src[names[x]];
+	}
+})(); // end globals protection closure
+
+dojo.logging.log = new dojo.logging.Logger();
+
+// an associative array of logger objects. This object inherits from
+// a list of level names with their associated numeric levels
+dojo.logging.log.levels = [ {"name": "DEBUG", "level": 1},
+						   {"name": "INFO", "level": 2},
+						   {"name": "WARNING", "level": 3},
+						   {"name": "ERROR", "level": 4},
+						   {"name": "CRITICAL", "level": 5} ];
+
+dojo.logging.log.loggers = {};
+
+dojo.logging.log.getLogger = function(name){
+	if(!this.loggers[name]){
+		this.loggers[name] = new dojo.logging.Logger();
+		this.loggers[name].parent = this;
+	}
+	return this.loggers[name];
+}
+
+dojo.logging.log.getLevelName = function(lvl){
+	for(var x=0; x<this.levels.length; x++){
+		if(this.levels[x].level == lvl){
+			return this.levels[x].name;
+		}
+	}
+	return null;
+}
+
+dojo.logging.log.addLevelName = function(name, lvl){
+	if(this.getLevelName(name)){
+		this.err("could not add log level "+name+" because a level with that name already exists");
+		return false;
+	}
+	this.levels.append({"name": name, "level": parseInt(lvl)});
+	return true;
+}
+
+dojo.logging.log.getLevel = function(name){
+	for(var x=0; x<this.levels.length; x++){
+		if(this.levels[x].name.toUpperCase() == name.toUpperCase()){
+			return this.levels[x].level;
+		}
+	}
+	return null;
+}
+
+// a default handler class, it simply saves all of the handle()'d records in
+// memory. Useful for attaching to with dojo.event.connect()
+dojo.logging.MemoryLogHandler = function(level, recordsToKeep, postType, postInterval){
+	// mixin style inheritance
+	dojo.logging.LogHandler.call(this, level);
+	// default is unlimited
+	this.numRecords = (typeof djConfig['loggingNumRecords'] != 'undefined') ? djConfig['loggingNumRecords'] : ((recordsToKeep) ? recordsToKeep : -1);
+	// 0=count, 1=time, -1=don't post TODO: move this to a better location for prefs
+	this.postType = (typeof djConfig['loggingPostType'] != 'undefined') ? djConfig['loggingPostType'] : ( postType || -1);
+	// milliseconds for time, interger for number of records, -1 for non-posting,
+	this.postInterval = (typeof djConfig['loggingPostInterval'] != 'undefined') ? djConfig['loggingPostInterval'] : ( postType || -1);
+	
+}
+// prototype inheritance
+dojo.logging.MemoryLogHandler.prototype = new dojo.logging.LogHandler();
+
+// FIXME
+// dojo.inherits(dojo.logging.MemoryLogHandler, 
+
+// over-ride base-class
+dojo.logging.MemoryLogHandler.prototype.emit = function(record){
+	this.data.push(record);
+	if(this.numRecords != -1){
+		while(this.data.length>this.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){
+	// we should probably abstract this in the future
+	var logStr = String(dojo.log.getLevelName(record.level)+": "+record.time.toLocaleTimeString())+": "+record.message;
+	if(!dj_undef("debug", dj_global)){
+		dojo.debug(logStr);
+	}else if((typeof dj_global["print"] == "function")&&(!dojo.render.html.capable)){
+		print(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;


Property changes on: trunk/PhotoOp/root/static/js/src/logging/Logger.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/logging/__package__.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/logging/__package__.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/logging/__package__.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -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.kwCompoundRequire({
+	common: ["dojo.logging.Logger", false, false],
+	rhino: ["dojo.logging.RhinoLogger"]
+});
+dojo.provide("dojo.logging.*");


Property changes on: trunk/PhotoOp/root/static/js/src/logging/__package__.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/math/Math.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/math/Math.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/math/Math.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -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");


Property changes on: trunk/PhotoOp/root/static/js/src/math/Math.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/math/__package__.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/math/__package__.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/math/__package__.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,18 @@
+/*
+	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.kwCompoundRequire({
+	common: [
+		["dojo.math", false, false],
+		["dojo.math.curves", false, false],
+		["dojo.math.points", false, false]
+	]
+});
+dojo.provide("dojo.math.*");


Property changes on: trunk/PhotoOp/root/static/js/src/math/__package__.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/math/curves.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/math/curves.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/math/curves.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,222 @@
+/*
+	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.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<this.dimensions;i++)
+				retVal[i] = ((this.end[i] - this.start[i]) * n) + this.start[i];
+			return retVal;
+		}
+
+		return this;
+	},
+
+
+	//Takes an array of points, the first is the start point, the last is end point and the ones in
+	//between are the Bezier control points.
+	Bezier: function(pnts) {
+		this.getValue = function(step) {
+			if(step >= 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[0].length;k++) { retVal[k]=0; }
+			for(var j=0;j<this.p[0].length;j++) {
+				var C=0; var D=0;
+				for(var i=0;i<this.p.length;i++) {
+					C += this.p[i][j] * this.p[this.p.length-1][0]
+						* dojo.math.bernstein(step,this.p.length,i);
+				}
+				for(var l=0;l<this.p.length;l++) {
+					D += this.p[this.p.length-1][0] * dojo.math.bernstein(step,this.p.length,l);
+				}
+				retVal[j] = C/D;
+			}
+			return retVal;
+		}
+		this.p = pnts;
+		return this;
+	},
+
+
+	//Catmull-Rom Spline - allows you to interpolate a smooth curve through a set of points in n-dimensional space
+	CatmullRom : function(pnts,c) {
+		this.getValue = function(step) {
+			var percent = step * (this.p.length-1);
+			var node = Math.floor(percent);
+			var progress = percent - node;
+
+			var i0 = node-1; if(i0 < 0) i0 = 0;
+			var i = node;
+			var i1 = node+1; if(i1 >= 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<this.p[0].length;k++) {
+				var x1 = ( -this.c * this.p[i0][k] ) + ( (2 - this.c) * this.p[i][k] ) + ( (this.c-2) * this.p[i1][k] ) + ( this.c * this.p[i2][k] );
+				var x2 = ( 2 * this.c * this.p[i0][k] ) + ( (this.c-3) * this.p[i][k] ) + ( (3 - 2 * this.c) * this.p[i1][k] ) + ( -this.c * this.p[i2][k] );
+				var x3 = ( -this.c * this.p[i0][k] ) + ( this.c * this.p[i1][k] );
+				var x4 = this.p[i][k];
+
+				retVal[k] = x1*u3 + x2*u2 + x3*u + x4;
+			}
+			return retVal;
+
+		}
+
+
+		if(!c) this.c = 0.7;
+		else this.c = c;
+		this.p = pnts;
+
+		return this;
+	},
+
+	// FIXME: This is the bad way to do a partial-arc with 2 points. We need to have the user
+	// supply the radius, otherwise we always get a half-circle between the two points.
+	Arc : function(start, end, ccw) {
+		var center = dojo.math.points.midpoint(start, end);
+		var sides = dojo.math.points.translate(dojo.math.points.invert(center), start);
+		var rad = Math.sqrt(Math.pow(sides[0], 2) + Math.pow(sides[1], 2));
+		var theta = dojo.math.radToDeg(Math.atan(sides[1]/sides[0]));
+		if( sides[0] < 0 ) {
+			theta -= 90;
+		} else {
+			theta += 90;
+		}
+		dojo.math.curves.CenteredArc.call(this, center, rad, theta, theta+(ccw?-180:180));
+	},
+
+	// Creates an arc object, with center and radius (Top of arc = 0 degrees, increments clockwise)
+	//  center => 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;
+	}
+};


Property changes on: trunk/PhotoOp/root/static/js/src/math/curves.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/math/matrix.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/math/matrix.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/math/matrix.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,305 @@
+/*
+	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.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<ay; k++){
+		c[k] = [];
+		for(var i=0; i<bx; i++){
+
+			c[k][i] = 0;
+
+			for(var m=0; m<ax; m++){
+
+				c[k][i] += a[k][m]*b[m][i];
+			}
+		}
+	}
+
+	return c;
+}
+
+dojo.math.matrix.inverse = function(a){
+
+	a = dojo.math.matrix.copy(a);
+
+	// Formula used to Calculate Inverse:
+	// inv(A) = 1/det(A) * adj(A)
+
+	var tms = a.length;
+
+	var m = dojo.math.matrix.create(tms, tms);
+	var mm = dojo.math.matrix.adjoint(a);
+
+	var det = dojo.math.matrix.determinant(a);
+	var dd = 0;
+
+	if (det == 0){
+		dojo.debug("Determinant Equals 0, Not Invertible.");
+		return [[0]];
+	}else{
+		dd = 1 / det;
+	}
+
+	for (var i = 0; i < tms; i++)
+		for (var j = 0; j < tms; j++) {
+			m[i][j] = dd * mm[i][j];
+		}
+
+	return m;
+}
+
+dojo.math.matrix.determinant = function(a){
+
+	a = dojo.math.matrix.copy(a);
+
+	if (a.length != a[0].length){
+		dojo.debug("Can't calculate the determiant of a non-squre matrix!");
+		return 0;
+	}
+
+	var tms = a.length;
+	var det = 1;
+
+	var b = dojo.math.matrix.upperTriangle(a);
+
+	for (var i=0; i < tms; i++){
+		det *= b[i][i];
+	}
+
+	det = det * dojo.math.matrix.iDF;
+
+	return det;
+}
+
+dojo.math.matrix.upperTriangle = function(m){
+
+	m = dojo.math.matrix.copy(m);
+
+	var f1 = 0;
+	var temp = 0;
+	var tms = m.length;
+	var v = 1;
+
+	dojo.math.matrix.iDF = 1;
+
+	for (var col = 0; col < tms - 1; col++) {
+		for (var row = col + 1; row < tms; row++) {
+			v = 1;
+
+			var stop_loop = 0;
+
+			// check if 0 in diagonal
+ 			while ((m[col][col] == 0) && !stop_loop){
+
+				// if so switch until not
+				if (col + v >= 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; i<b; i++){
+		m[i] = [];
+		for(var j=0; j<a; j++){
+			m[i][j] = 0;
+		}
+	}
+	return m;
+}
+
+dojo.math.matrix.adjoint = function(a){
+
+	a = dojo.math.matrix.copy(a);
+
+	var tms = a.length;
+
+	if (a.length != a[0].length){
+		dojo.debug("Can't find the adjoint of a non-square matrix");
+		return [[0]];
+	}
+
+	if (tms == 1){
+		dojo.debug("Can't find the adjoint of a 1x1 matrix");
+		return [[0]];
+	}
+
+	var m = dojo.math.matrix.create(tms, tms);
+
+	var ii = 0;
+	var jj = 0;
+	var ia = 0;
+	var ja = 0;
+	var det = 0;
+
+	for (var i = 0; i < tms; i++){
+		for (var j = 0; j < tms; j++){
+
+			ia = 0;
+			ja = 0;
+
+			var ap = dojo.math.matrix.create(tms-1, tms-1);
+
+			for (ii = 0; ii < tms; ii++) {
+				for (jj = 0; jj < tms; jj++) {
+
+					if ((ii != i) && (jj != j)) {
+						ap[ia][ja] = a[ii][jj];
+						ja++;
+					}
+
+				}
+
+				if ((ii != i) && (jj != j)) {
+					ia++;
+				}
+				ja = 0;
+			}
+
+			det = dojo.math.matrix.determinant(ap);
+			m[i][j] = Math.pow(-1 , (i + j)) * det;
+		}
+	}
+
+	m = dojo.math.matrix.transpose(m);
+
+	return m;
+}
+
+dojo.math.matrix.transpose = function(a){
+
+	a = dojo.math.matrix.copy(a);
+
+	var m = dojo.math.matrix.create(a.length, a[0].length);
+
+	for (var i = 0; i < a.length; i++)
+		for (var j = 0; j < a[i].length; j++)
+			m[j][i] = a[i][j];
+	return m;
+}
+
+dojo.math.matrix.format = function(a){
+
+	function format_int(x){
+		var dp = 5;
+		var fac = Math.pow(10 , dp);
+		var a = Math.round(x*fac)/fac;
+		var b = a.toString();
+		if (b.charAt(0) != '-'){ b = ' ' + b;}
+		var has_dp = 0;
+		for(var i=1; i<b.length; i++){
+			if (b.charAt(i) == '.'){ has_dp = 1; }
+		}
+		if (!has_dp){ b += '.'; }
+		while(b.length < dp+3){ b += '0'; }
+		return b;
+	}
+
+	var ya = a.length;
+	var xa = a[0].length;
+
+	var buffer = '';
+
+	for (var y=0; y<ya; y++){
+		buffer += '| ';
+		for (var x=0; x<xa; x++){
+			buffer += format_int(a[y][x]) + ' ';
+		}
+		buffer += '|\n';
+	}
+
+	return buffer;
+}
+
+dojo.math.matrix.copy = function(a){
+
+	var ya = a.length;
+	var xa = a[0].length;
+
+	var m = dojo.math.matrix.create(xa, ya);
+
+	for (var y=0; y<ya; y++){
+		for (var x=0; x<xa; x++){
+			m[y][x] = a[y][x];
+		}
+	}
+
+	return m;
+}
+
+dojo.math.matrix.scale = function(k, a){
+
+	a = dojo.math.matrix.copy(a);
+
+	var ya = a.length;
+	var xa = a[0].length;
+
+	for (var y=0; y<ya; y++){
+		for (var x=0; x<xa; x++){
+			a[y][x] *= k;
+		}
+	}
+
+	return a;
+}


Property changes on: trunk/PhotoOp/root/static/js/src/math/matrix.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/math/points.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/math/points.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/math/points.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -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.math.points");
+dojo.require("dojo.math");
+
+// TODO: add a Point class?
+dojo.math.points = {
+	translate: function(a, b) {
+		if( a.length != b.length ) {
+			dojo.raise("dojo.math.translate: points not same size (a:[" + a + "], b:[" + b + "])");
+		}
+		var c = new Array(a.length);
+		for(var i = 0; i < a.length; i++) {
+			c[i] = a[i] + b[i];
+		}
+		return c;
+	},
+
+	midpoint: function(a, b) {
+		if( a.length != b.length ) {
+			dojo.raise("dojo.math.midpoint: points not same size (a:[" + a + "], b:[" + b + "])");
+		}
+		var c = new Array(a.length);
+		for(var i = 0; i < a.length; i++) {
+			c[i] = (a[i] + b[i]) / 2;
+		}
+		return c;
+	},
+
+	invert: function(a) {
+		var b = new Array(a.length);
+		for(var i = 0; i < a.length; i++) { b[i] = -a[i]; }
+		return b;
+	},
+
+	distance: function(a, b) {
+		return Math.sqrt(Math.pow(b[0]-a[0], 2) + Math.pow(b[1]-a[1], 2));
+	}
+};


Property changes on: trunk/PhotoOp/root/static/js/src/math/points.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/math.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/math.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/math.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,134 @@
+/*
+	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.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;
+}


Property changes on: trunk/PhotoOp/root/static/js/src/math.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/profile.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/profile.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/profile.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,117 @@
+/*
+	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.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; x<cols.length; x++){
+			var ntd = hdrtr.insertCell(x);
+			with(ntd.style){
+				backgroundColor = "#225d94";
+				color = "white";
+				borderBottom = "1px solid black";
+				borderRight = "1px solid black";
+				fontFamily = "tahoma";
+				fontWeight = "bolder";
+				paddingLeft = paddingRight = "5px";
+			}
+			ntd.appendChild(document.createTextNode(cols[x]));
+		}
+
+		for(var x=0; x < pns.length; x++){
+			var prf = profiles[pns[x]];
+			this.end(pns[x]);
+			if(prf.iters>0){
+				var bdytr = tbl.insertRow(true);
+				var vals = [pns[x], prf.iters, prf.total, parseInt(prf.total/prf.iters)];
+				for(var y=0; y<vals.length; y++){
+					var cc = bdytr.insertCell(y);
+					cc.appendChild(document.createTextNode(vals[y]));
+					with(cc.style){
+						borderBottom = "1px solid gray";
+						paddingLeft = paddingRight = "5px";
+						if(x%2){
+							backgroundColor = "#e1f1ff";
+						}
+						if(y>0){
+							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")){
+				document.body.replaceChild(ne, document.getElementById("profileOutputTable"));
+			}else{
+				document.body.appendChild(ne);
+			}
+			ne.appendChild(tbl);
+		}
+
+		return tbl;
+	}
+}


Property changes on: trunk/PhotoOp/root/static/js/src/profile.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/reflect/__package__.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/reflect/__package__.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/reflect/__package__.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -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.*");


Property changes on: trunk/PhotoOp/root/static/js/src/reflect/__package__.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/reflect/reflection.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/reflect/reflection.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/reflect/reflection.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -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");


Property changes on: trunk/PhotoOp/root/static/js/src/reflect/reflection.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/regexp.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/regexp.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/regexp.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -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.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 <mailto:foo at yahoo.com>.  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 = "<?(mailto\\:)?" + 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.
+
+  @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 = ""; }
+
+	// build sign RE
+	var signRE = dojo.regexp.buildGroupRE(flags.signed,
+		function(q) { if (q) { return "[-+]"; }  return ""; }
+	);
+
+	// number RE
+	var numberRE = dojo.regexp.buildGroupRE(flags.separator,
+		function(sep) { 
+			if ( sep == "" ) { 
+				return "(0|[1-9]\\d*)"; 
+			}
+			return "(0|[1-9]\\d{0,2}([" + sep + "]\\d{3})*)"; 
+		}
+	);
+	var numberRE;
+
+	// 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.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.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 = "";
+	if ( flags.places == Infinity) { 
+		decimalRE = "(\\" + flags.decimal + "\\d+)?"; 
+	}
+	else if ( flags.places > 0) { 
+		decimalRE = "\\" + flags.decimal + "\\d{" + flags.places + "}"; 
+	}
+
+	// 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.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.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.cents  The two decimal places for cents.  Can be true, false, or [true, false].
+      Default is [true, false], (i.e. will match if cents are present are not).
+    flags.decimal  A string for the character used as the decimal point.  Default is ".".
+
+  @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.separator != "string") { flags.separator = ","; }
+	if (typeof flags.cents == "undefined") { flags.cents = [true, false]; }
+	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?";
+		}
+	);
+
+	// number RE
+	var numberRE = dojo.regexp.integer( {signed: false, separator: flags.separator} );
+
+	// build cents RE
+	var centsRE = dojo.regexp.buildGroupRE(flags.cents,
+		function(q) { if (q) { return "(\\" + flags.decimal + "\\d\\d)"; }  return ""; }
+	);
+
+	// build currency RE
+	var currencyRE;
+	if (flags.placement == "before") {
+		currencyRE = signRE + symbolRE + numberRE + centsRE;
+	}
+	else {
+		currencyRE = signRE + numberRE + centsRE + symbolRE;
+	}
+
+	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("|") + ")";
+}


Property changes on: trunk/PhotoOp/root/static/js/src/regexp.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/rpc/Deferred.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/rpc/Deferred.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/rpc/Deferred.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -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.provide("dojo.rpc.Deferred");
+dojo.require("dojo.Deferred");
+
+dojo.rpc.Deferred = dojo.Deferred;
+dojo.rpc.Deferred.prototype = dojo.Deferred.prototype;


Property changes on: trunk/PhotoOp/root/static/js/src/rpc/Deferred.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/rpc/JotService.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/rpc/JotService.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/rpc/JotService.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,41 @@
+/*
+	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.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);
+	}
+});


Property changes on: trunk/PhotoOp/root/static/js/src/rpc/JotService.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/rpc/JsonService.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/rpc/JsonService.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/rpc/JsonService.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,103 @@
+/*
+	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.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"]||obj["result"]){
+			return obj["result"]||obj["Result"];
+		}else if(obj["ResultSet"]){
+			return obj["ResultSet"];
+		}else{
+			return obj;
+		}
+	}
+});


Property changes on: trunk/PhotoOp/root/static/js/src/rpc/JsonService.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/rpc/RpcService.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/rpc/RpcService.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/rpc/RpcService.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,116 @@
+/*
+	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.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
+		});		
+	}
+});


Property changes on: trunk/PhotoOp/root/static/js/src/rpc/RpcService.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/rpc/YahooService.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/rpc/YahooService.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/rpc/YahooService.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,55 @@
+/*
+	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.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 initializae 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.scrictArgChecks = 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
+		});
+	}
+});


Property changes on: trunk/PhotoOp/root/static/js/src/rpc/YahooService.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/rpc/__package__.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/rpc/__package__.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/rpc/__package__.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,14 @@
+/*
+	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.kwCompoundRequire({
+	common: ["dojo.rpc.JsonService", false, false]
+});
+dojo.provide("dojo.rpc.*");


Property changes on: trunk/PhotoOp/root/static/js/src/rpc/__package__.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/rpc/yahoo.smd
===================================================================
--- trunk/PhotoOp/root/static/js/src/rpc/yahoo.smd	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/rpc/yahoo.smd	2009-02-07 13:12:54 UTC (rev 5416)
@@ -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" },
+			]
+		}
+		*/
+	]
+}


Property changes on: trunk/PhotoOp/root/static/js/src/rpc/yahoo.smd
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/selection/Selection.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/selection/Selection.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/selection/Selection.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,425 @@
+/*
+	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.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(item, this.selection);
+		} else {
+			return dojo.lang.find(item, this.items);
+		}
+	},
+
+	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(item, this._pivotItems);
+		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;
+	}
+});


Property changes on: trunk/PhotoOp/root/static/js/src/selection/Selection.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/storage/Storage.as
===================================================================
--- trunk/PhotoOp/root/static/js/src/storage/Storage.as	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/storage/Storage.as	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,145 @@
+/*
+	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
+*/
+
+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);
+		_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(); 
+	}
+}
+


Property changes on: trunk/PhotoOp/root/static/js/src/storage/Storage.as
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/storage/__package__.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/storage/__package__.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/storage/__package__.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,17 @@
+/*
+	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.kwCompoundRequire({
+	common: ["dojo.storage"],
+	browser: ["dojo.storage.browser"],
+	dashboard: ["dojo.storage.dashboard"]
+});
+dojo.provide("dojo.storage.*");
+


Property changes on: trunk/PhotoOp/root/static/js/src/storage/__package__.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/storage/browser.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/storage/browser.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/storage/browser.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,199 @@
+/*
+	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.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 Alex Russel, alex at dojotoolkit.org
+		@author Brad Neuberg, bkn3 at 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){
+		//dojo.debug("_onStatus, statusResult="+statusResult+", key="+key);
+		if(statusResult == dojo.storage.PENDING){
+			dojo.flash.obj.center();
+			dojo.flash.obj.setVisible(true);
+		}else{
+			dojo.flash.obj.setVisible(false);
+		}
+		
+		if(!dojo.lang.isUndefined(dojo.storage._statusHandler) 
+				&& dojo.storage._statusHandler != null){
+			dojo.storage._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();
+															


Property changes on: trunk/PhotoOp/root/static/js/src/storage/browser.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/storage/dashboard.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/storage/dashboard.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/storage/dashboard.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,52 @@
+/*
+	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.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());


Property changes on: trunk/PhotoOp/root/static/js/src/storage/dashboard.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/storage/storage_dialog.fla
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/storage/storage_dialog.fla
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/storage.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/storage.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/storage.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,418 @@
+/*
+	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
+*/
+
+/** 
+		FIXME: Write better docs.
+
+		@author Alex Russel, alex at dojotoolkit.org
+		@author Brad Neuberg, bkn3 at 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.storage = function(){
+}
+
+dojo.lang.extend(dojo.storage, {
+	/** 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,
+
+	/** 
+	  Allows this storage provider to initialize itself. This is called
+	  after the page has finished loading, so you can not do document.writes(). 
+	*/
+	initialize: function(){
+	 dojo.unimplemented("dojo.storage.initialize");
+	},
+	
+	/** 
+	  Returns whether this storage provider is 
+	  available on this platform. 
+	
+	  @returns True or false if this storage 
+	  provider is supported.
+	*/
+	isAvailable: function(){
+		dojo.unimplemented("dojo.storage.isAvailable");
+	},
+	
+	/**
+	  Puts a key and value into this storage system.
+
+	  @param key A string key to use when retrieving 
+	         this value in the future.
+	  @param value A value to store; this can be 
+	         any JavaScript type.
+	  @param 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);	
+	*/
+	put: function(key, value, resultsHandler){ 
+    dojo.unimplemented("dojo.storage.put");
+  },
+
+	/**
+	  Gets the value with the given key. Returns null
+	  if this key is not in the storage system.
+	
+	  @param key A string key to get the value of.
+	  @returns Returns any JavaScript object type; 
+	  null if the key is not
+	  present. 
+	*/
+	get: function(key){
+    dojo.unimplemented("dojo.storage.get");
+  },
+
+	/**
+	  Determines whether the storage has the given 
+	  key. 
+	
+	    @returns Whether this key is 
+	             present or not. 
+	*/
+	hasKey: function(key){
+		if (this.get(key) != null)
+			return true;
+		else
+			return false;
+	},
+
+	/**
+	  Enumerates all of the available keys in 
+	  this storage system.
+	
+	  @returns Array of string keys in this 
+	           storage system.
+	*/
+	getKeys: function(){
+    dojo.unimplemented("dojo.storage.getKeys");
+  },
+
+	/**
+	  Completely clears this storage system of all 
+	  of it's values and keys. 
+	*/
+	clear: function(){
+    dojo.unimplemented("dojo.storage.clear");
+  },
+  
+  /** Removes the given key from the storage system. */
+  remove: function(key){
+  	dojo.unimplemented("dojo.storage.remove");
+  },
+
+	/**
+	  Returns whether this storage provider's 
+	  values are persisted when this platform 
+	  is shutdown. 
+	
+	  @returns True or false whether this 
+	  storage is permanent. 
+	*/
+	isPermanent: function(){
+		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");
+  },
+
+	/**
+	  Determines whether this provider has a 
+	  settings UI.
+	
+	  @returns True or false if this provider has 
+	           the ability to show a
+	           a settings UI to change it's 
+	           values, change the amount of storage
+	           available, etc. 
+	*/
+	hasSettingsUI: function(){
+		return false;
+	},
+
+	/**
+	  If this provider has a settings UI, it is 
+	  shown. 
+	*/
+	showSettingsUI: function(){
+	 dojo.unimplemented("dojo.storage.showSettingsUI");
+	},
+
+	/**
+	  If this provider has a settings UI, hides
+	  it.
+	*/
+	hideSettingsUI: function(){
+	 dojo.unimplemented("dojo.storage.hideSettingsUI");
+	},
+	
+	/** 
+	  The provider name as a string, such as 
+	  "dojo.storage.FlashStorageProvider". 
+	*/
+	getType: function(){
+		dojo.unimplemented("dojo.storage.getType");
+	},
+	
+	/**
+	  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. 
+	*/
+	isValidKey: function(keyName){
+		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";
+	
+	/** Initializes the storage system. */
+	this.initialize = function(){
+		// autodetect the best storage provider we can provide on this platform
+		this.autodetect();
+	}
+	
+	/**
+	  Registers the existence of a new storage provider; used by subclasses
+	  to inform the manager of their existence. 
+	
+	  @param name The full class name of this provider, such as 
+	  "dojo.storage.browser.Flash6StorageProvider".
+	  @param instance An instance of this provider, which we will use to
+	  call isAvailable() on. 
+	*/
+	this.register = function(name, instance) {
+		this.providers[this.providers.length] = instance;
+		this.providers[name] = instance;
+	}
+	
+	/**
+	  Instructs the storageManager to use 
+	  the given storage class for all storage requests.
+	    
+	  Example:
+	    
+	  dojo.storage.setProvider(
+	         dojo.storage.browser.IEStorageProvider)
+	*/
+	this.setProvider = function(storageClass){
+	
+	}
+	
+	/** 
+	  Autodetects the best possible persistent
+	  storage provider available on this platform. 
+	*/
+	this.autodetect = function(){
+		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;
+	}
+	
+	/** Returns whether any storage options are available. */
+	this.isAvailable = function(){
+		return this.available;
+	}
+	
+	/** 
+	 	Returns whether the storage system is initialized and
+	 	ready to be used. 
+	*/
+	this.isInitialized = function(){
+		// FIXME: This should _really_ not be in here, but it fixes a bug
+		if(dojo.flash.ready == false){
+			return false;
+		}else{
+			return this.initialized;
+		}
+	}
+
+	/**
+	  Determines if this platform supports
+	  the given storage provider.
+	
+	  Example:
+			
+	  dojo.storage.manager.supportsProvider(
+	    "dojo.storage.browser.InternetExplorerStorageProvider");
+	*/
+	this.supportsProvider = function(storageClass){
+		// 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;
+	}
+	
+	/** 
+	  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:
+	
+	  dojo.connect(dojo.storage.manager, "loaded", someInstance, 
+	               someInstance.someMethod);
+	*/
+	this.loaded = function(){
+	}
+}


Property changes on: trunk/PhotoOp/root/static/js/src/storage.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/string/Builder.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/string/Builder.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/string/Builder.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,105 @@
+/*
+	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.string.Builder");
+dojo.require("dojo.string");
+
+// NOTE: testing shows that direct "+=" concatenation is *much* faster on
+// Spidermoneky and Rhino, while arr.push()/arr.join() style concatenation is
+// significantly quicker on IE (Jscript/wsh/etc.).
+
+dojo.string.Builder = function(str){
+	this.arrConcat = (dojo.render.html.capable && dojo.render.html["ie"]);
+
+	var a = [];
+	var b = str || "";
+	var length = this.length = b.length;
+
+	if(this.arrConcat){
+		if(b.length > 0){
+			a.push(b);
+		}
+		b = "";
+	}
+
+	this.toString = this.valueOf = function(){ 
+		return (this.arrConcat) ? a.join("") : b;
+	};
+
+	this.append = function(s){
+		if(this.arrConcat){
+			a.push(s);
+		}else{
+			b+=s;
+		}
+		length += s.length;
+		this.length = length;
+		return this;
+	};
+
+	this.clear = function(){
+		a = [];
+		b = "";
+		length = this.length = 0;
+		return this;
+	};
+
+	this.remove = function(f,l){
+		var s = ""; 
+		if(this.arrConcat){
+			b = a.join(""); 
+		}
+		a=[];
+		if(f>0){
+			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;
+	};
+};


Property changes on: trunk/PhotoOp/root/static/js/src/string/Builder.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/string/__package__.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/string/__package__.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/string/__package__.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,19 @@
+/*
+	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.kwCompoundRequire({
+	common: [
+		"dojo.string",
+		"dojo.string.common",
+		"dojo.string.extras",
+		"dojo.string.Builder"
+	]
+});
+dojo.provide("dojo.string.*");


Property changes on: trunk/PhotoOp/root/static/js/src/string/__package__.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/string/common.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/string/common.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/string/common.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,87 @@
+/*
+	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.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);
+}


Property changes on: trunk/PhotoOp/root/static/js/src/string/common.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/string/extras.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/string/extras.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/string/extras.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,237 @@
+/*
+	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.string.extras");
+
+dojo.require("dojo.string.common");
+dojo.require("dojo.lang");
+
+/**
+ * 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);
+	});
+};
+
+/**
+ * Parameterized string function
+ * str - formatted string with %{values} to be replaces
+ * pairs - object of name: "value" value pairs
+ * killExtra - remove all remaining %{values} after pairs are inserted
+ */
+dojo.string.paramString = function(str, pairs, killExtra) {
+	dojo.deprecated("dojo.string.paramString",
+		"use dojo.string.substituteParams instead", "0.4");
+
+	for(var name in pairs) {
+		var re = new RegExp("\\%\\{" + name + "\\}", "g");
+		str = str.replace(re, pairs[name]);
+	}
+
+	if(killExtra) { str = str.replace(/%\{([^\}\s]+)\}/g, ""); }
+	return str;
+}
+
+/** 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<words.length; i++){
+		words[i] = words[i].charAt(0).toUpperCase() + words[i].substring(1);
+	}
+	return words.join(" ");
+}
+
+/**
+ * Return true if the entire string is whitespace characters
+ */
+dojo.string.isBlank = function (str) {
+	if(!dojo.lang.isString(str)) { return true; }
+	return (dojo.string.trim(str).length == 0);
+}
+
+dojo.string.encodeAscii = function(str) {
+	if(!dojo.lang.isString(str)) { return str; }
+	var ret = "";
+	var value = escape(str);
+	var match, re = /%u([0-9A-F]{4})/i;
+	while((match = value.match(re))) {
+		var num = Number("0x"+match[1]);
+		var newVal = escape("&#" + num + ";");
+		ret += value.substring(0, match.index) + newVal;
+		value = value.substring(match.index+match[0].length);
+	}
+	ret += value.replace(/\+/g, "%2B");
+	return ret;
+}
+
+dojo.string.escape = function(type, str) {
+	var args = dojo.lang.toArray(arguments, 1);
+	switch(type.toLowerCase()) {
+		case "xml":
+		case "html":
+		case "xhtml":
+			return dojo.string.escapeXml.apply(this, args);
+		case "sql":
+			return dojo.string.escapeSql.apply(this, args);
+		case "regexp":
+		case "regex":
+			return dojo.string.escapeRegExp.apply(this, args);
+		case "javascript":
+		case "jscript":
+		case "js":
+			return dojo.string.escapeJavaScript.apply(this, args);
+		case "ascii":
+			// so it's encode, but it seems useful
+			return dojo.string.encodeAscii.apply(this, args);
+		default:
+			return str;
+	}
+}
+
+dojo.string.escapeXml = function(str, noSingleQuotes) {
+	str = str.replace(/&/gm, "&amp;").replace(/</gm, "&lt;")
+		.replace(/>/gm, "&gt;").replace(/"/gm, "&quot;");
+	if(!noSingleQuotes) { str = str.replace(/'/gm, "&#39;"); }
+	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;
+}


Property changes on: trunk/PhotoOp/root/static/js/src/string/extras.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/string.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/string.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/string.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -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.provide("dojo.string");
+dojo.require("dojo.string.common");


Property changes on: trunk/PhotoOp/root/static/js/src/string.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/style.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/style.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/style.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,826 @@
+/*
+	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.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 sizing = ds.getStyle(node, "-moz-box-sizing");
+			if(!sizing){ sizing = ds.getStyle(node, "box-sizing"); }
+			return (sizing ? sizing : bs.CONTENT_BOX);
+		}
+	}
+
+	/*
+
+	The following several function use the dimensions shown below
+
+		+-------------------------+
+		|  margin                 |
+		| +---------------------+ |
+		| |  border             | |
+		| | +-----------------+ | |
+		| | |  padding        | | |
+		| | | +-------------+ | | |
+		| | | |   content   | | | |
+		| | | +-------------+ | | |
+		| | +-|-------------|-+ | |
+		| +-|-|-------------|-|-+ |
+		+-|-|-|-------------|-|-|-+
+		| | | |             | | | |
+		| | | |<- content ->| | | |
+		| |<------ inner ------>| |
+		|<-------- outer -------->|
+		+-------------------------+
+
+		* content-box
+
+		|m|b|p|             |p|b|m|
+		| |<------ offset ----->| |
+		| | |<---- client --->| | |
+		| | | |<-- width -->| | | |
+
+		* border-box
+
+		|m|b|p|             |p|b|m|
+		| |<------ offset ----->| |
+		| | |<---- client --->| | |
+		| |<------ width ------>| |
+	*/
+
+	/*
+		Notes:
+
+		General:
+			- Uncomputable values are returned as NaN.
+			- setOuterWidth/Height return *false* if the outer size could not
+			  be computed, otherwise *true*.
+			- (sjmiles) knows no way to find the calculated values for auto-margins. 
+			- All returned values are floating point in 'px' units. If a
+			  non-zero computed style value is not specified in 'px', NaN is
+			  returned.
+
+		FF:
+			- styles specified as '0' (unitless 0) show computed as '0pt'.
+
+		IE:
+			- clientWidth/Height are unreliable (0 unless the object has 'layout').
+			- margins must be specified in px, or 0 (in any unit) for any
+			  sizing function to work. Otherwise margins detect as 'auto'.
+			- padding can be empty or, if specified, must be in px, or 0 (in
+			  any unit) for any sizing function to work.
+
+		Safari:
+			- Safari defaults padding values to 'auto'.
+
+		See the unit tests for examples of (un)computable values in a given browser.
+
+	*/
+
+	// FIXME: these work for some elements (e.g. DIV) but not others (e.g. TABLE, TEXTAREA)
+
+	ds.isBorderBox = function(node){
+		return (ds.getBoxSizing(node) == bs.BORDER_BOX);
+	}
+
+	ds.getUnitValue = function(node, cssSelector, autoIsZero){
+		var s = ds.getComputedStyle(node, cssSelector);
+		if((!s)||((s == 'auto')&&(autoIsZero))){ return { value: 0, units: 'px' }; }
+		if(dojo.lang.isUndefined(s)){return ds.getUnitValue.bad;}
+		// FIXME: is regex inefficient vs. parseInt or some manual test? 
+		var match = s.match(/(\-?[\d.]+)([a-z%]*)/i);
+		if (!match){return ds.getUnitValue.bad;}
+		return { value: Number(match[1]), units: match[2].toLowerCase() };
+	}
+	// FIXME: 'bad' value should be 0?
+	ds.getUnitValue.bad = { value: NaN, units: '' };
+	
+	ds.getPixelValue = function(node, cssSelector, autoIsZero){
+		var result = ds.getUnitValue(node, cssSelector, autoIsZero);
+		// FIXME: there is serious debate as to whether or not this is the right solution
+		if(isNaN(result.value)){ return 0; }
+		// FIXME: code exists for converting other units to px (see Dean Edward's IE7) 
+		// but there are cross-browser complexities
+		if((result.value)&&(result.units != 'px')){ return NaN; }
+		return result.value;
+	}
+	
+	// FIXME: deprecated
+	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, selector, value){
+		if(isNaN(value)){return false;}
+		node.style[selector] = Math.max(0, value) + 'px'; 
+		return true;
+	}
+	
+	ds._sumPixelValues = function(node, selectors, autoIsZero){
+		var total = 0;
+		for(var x=0; x<selectors.length; x++){
+			total += ds.getPixelValue(node, selectors[x], autoIsZero);
+		}
+		return total;
+	}
+
+	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, pxWidth){
+		node = dojo.byId(node);
+		if (ds.isBorderBox(node)){
+			pxWidth += ds.getPadBorderWidth(node);
+		}
+		return ds.setPositivePixelValue(node, "width", pxWidth);
+	}
+
+	ds.setMarginBoxWidth = function(node, pxWidth){
+		node = dojo.byId(node);
+		if (!ds.isBorderBox(node)){
+			pxWidth -= ds.getPadBorderWidth(node);
+		}
+		pxWidth -= ds.getMarginWidth(node);
+		return ds.setPositivePixelValue(node, "width", pxWidth);
+	}
+
+	// FIXME: deprecate and remove
+	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; // FIXME: does this work?
+	}
+
+	ds.getMarginBoxHeight = function(node){
+		return ds.getInnerHeight(node) + ds.getMarginHeight(node);
+	}
+
+	ds.setContentBoxHeight = function(node, pxHeight){
+		node = dojo.byId(node);
+		if (ds.isBorderBox(node)){
+			pxHeight += ds.getPadBorderHeight(node);
+		}
+		return ds.setPositivePixelValue(node, "height", pxHeight);
+	}
+
+	ds.setMarginBoxHeight = function(node, pxHeight){
+		node = dojo.byId(node);
+		if (!ds.isBorderBox(node)){
+			pxHeight -= ds.getPadBorderHeight(node);
+		}
+		pxHeight -= ds.getMarginHeight(node);
+		return ds.setPositivePixelValue(node, "height", pxHeight);
+	}
+
+	// FIXME: deprecate and remove
+	ds.getContentHeight = ds.getContentBoxHeight;
+	ds.getInnerHeight = ds.getBorderBoxHeight;
+	ds.getOuterHeight = ds.getMarginBoxHeight;
+	ds.setContentHeight = ds.setContentBoxHeight;
+	ds.setOuterHeight = ds.setMarginBoxHeight;
+
+	/**
+	 * dojo.style.getAbsolutePosition(xyz, true) returns xyz's position relative to the document.
+	 * Itells you where you would position a node
+	 * inside document.body such that it was on top of xyz.  Most people set the flag to true when calling
+	 * getAbsolutePosition().
+	 *
+	 * dojo.style.getAbsolutePosition(xyz, false) returns xyz's position relative to the viewport.
+	 * It returns the position that would be returned
+	 * by event.clientX/Y if the mouse were directly over the top/left of this node.
+	 */
+	ds.getAbsolutePosition = ds.abs = function(node, includeScroll){
+		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){
+			// mozilla
+			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 endNode;		
+				// in Safari, if the node is an absolutely positioned child of
+				// the body and the body has a margin the offset of the child
+				// and the body contain the body's margins, so we need to end
+				// at the body
+				if(	(h.safari)&&
+					(node.style.getPropertyValue("position") == "absolute")&&
+					(node.parentNode == db)){
+					endNode = db;
+				}else{
+					endNode = 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 != endNode)&&(node != null));
+			}else if(node["x"]&&node["y"]){
+				ret.x += isNaN(node.x) ? 0 : node.x;
+				ret.y += isNaN(node.y) ? 0 : node.y;
+			}
+		}
+
+		// account for document scrolling!
+		if(includeScroll){
+			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; } // FIXME: throw an error?
+		
+		var retVal = 0;
+		while(node){
+			var val = node[prop];
+			if(val){
+				retVal += val - 0;
+				if(node==document.body){ break; }// opera and khtml #body & #html has the same values, we only need one value
+			}
+			node = node.parentNode;
+		}
+		return retVal;
+	}
+
+	ds.getTotalOffset = function(node, type, includeScroll){
+		return ds.abs(node, includeScroll)[(type == "top") ? "y" : "x"];
+	}
+
+	ds.getAbsoluteX = ds.totalOffsetLeft = function(node, includeScroll){
+		return ds.getTotalOffset(node, "left", includeScroll);
+	}
+
+	ds.getAbsoluteY = ds.totalOffsetTop = function(node, includeScroll){
+		return ds.getTotalOffset(node, "top", includeScroll);
+	}
+
+	ds.styleSheet = null;
+
+	// FIXME: this is a really basic stub for adding and removing cssRules, but
+	// it assumes that you know the index of the cssRule that you want to add 
+	// or remove, making it less than useful.  So we need something that can 
+	// search for the selector that you you want to remove.
+	ds.insertCssRule = function(selector, declaration, index) {
+		if (!ds.styleSheet) {
+			if (document.createStyleSheet) { // IE
+				ds.styleSheet = document.createStyleSheet();
+			} else if (document.styleSheets[0]) { // rest
+				// FIXME: should create a new style sheet here
+				// fall back on an exsiting style sheet
+				ds.styleSheet = document.styleSheets[0];
+			} else { return null; } // fail
+		}
+
+		if (arguments.length < 3) { // index may == 0
+			if (ds.styleSheet.cssRules) { // W3
+				index = ds.styleSheet.cssRules.length;
+			} else if (ds.styleSheet.rules) { // IE
+				index = ds.styleSheet.rules.length;
+			} else { return null; } // fail
+		}
+
+		if (ds.styleSheet.insertRule) { // W3
+			var rule = selector + " { " + declaration + " }";
+			return ds.styleSheet.insertRule(rule, index);
+		} else if (ds.styleSheet.addRule) { // IE
+			return ds.styleSheet.addRule(selector, declaration, index);
+		} else { return null; } // fail
+	}
+
+	ds.removeCssRule = function(index){
+		if(!ds.styleSheet){
+			dojo.debug("no stylesheet defined for removing rules");
+			return false;
+		}
+		if(h.ie){
+			if(!index){
+				index = ds.styleSheet.rules.length;
+				ds.styleSheet.removeRule(index);
+			}
+		}else if(document.styleSheets[0]){
+			if(!index){
+				index = ds.styleSheet.cssRules.length;
+			}
+			ds.styleSheet.deleteRule(index);
+		}
+		return true;
+	}
+
+	// calls css by XmlHTTP and inserts it into DOM as <style [widgetType="widgetType"]> *downloaded cssText*</style>
+	ds.insertCssFile = function(URI, doc, checkDuplicates){
+		if(!URI){ return; }
+		if(!doc){ doc = document; }
+		var cssStr = dojo.hostenv.getText(URI);
+		cssStr = ds.fixPathsInCssText(cssStr, URI);
+
+		if(checkDuplicates){
+			var styles = doc.getElementsByTagName("style");
+			var cssText = "";
+			for(var i = 0; i<styles.length; i++){
+				cssText = (styles[i].styleSheet && styles[i].styleSheet.cssText) ? styles[i].styleSheet.cssText : styles[i].innerHTML;
+				if(cssStr == cssText){ return; }
+			}
+		}
+
+		var style = ds.insertCssText(cssStr);
+		// insert custom attribute ex dbgHref="../foo.css" usefull when debugging in DOM inspectors, no?
+		if(style && djConfig.isDebug){
+			style.setAttribute("dbgHref", URI);
+		}
+		return style
+	}
+
+	// DomNode Style  = insertCssText(String ".dojoMenu {color: green;}"[, DomDoc document, dojo.uri.Uri Url ])
+	ds.insertCssText = function(cssStr, doc, URI){
+		if(!cssStr){ return; }
+		if(!doc){ doc = document; }
+		if(URI){// fix paths in cssStr
+			cssStr = ds.fixPathsInCssText(cssStr, URI);
+		}
+		var style = doc.createElement("style");
+		style.setAttribute("type", "text/css");
+		// IE is b0rken enough to require that we add the element to the doc
+		// before changing it's properties
+		var head = doc.getElementsByTagName("head")[0];
+		if(!head){ // must have a head tag 
+			dojo.debug("No head tag in document, aborting styles");
+			return;
+		}else{
+			head.appendChild(style);
+		}
+		if(style.styleSheet){// IE
+			style.styleSheet.cssText = cssStr;
+		}else{ // w3c
+			var cssText = doc.createTextNode(cssStr);
+			style.appendChild(cssText);
+		}
+		return style;
+	}
+
+	// String cssText = fixPathsInCssText(String cssStr, dojo.uri.Uri URI)
+	// usage: cssText comes from dojoroot/src/widget/templates/HtmlFoobar.css
+	// 	it has .dojoFoo { background-image: url(images/bar.png);} 
+	//	then uri should point to dojoroot/src/widget/templates/
+	ds.fixPathsInCssText = function(cssStr, URI){
+		if(!cssStr || !URI){ return; }
+		var pos = 0; var str = ""; var url = "";
+		while(pos!=-1){
+			pos = 0;url = "";
+			pos = cssStr.indexOf("url(", pos);
+			if(pos<0){ break; }
+			str += cssStr.slice(0,pos+4);
+			cssStr = cssStr.substring(pos+4, cssStr.length);
+			url += cssStr.match(/^[\t\s\w()\/.\\'"-:#=&?]*\)/)[0]; // url string
+			cssStr = cssStr.substring(url.length-1, cssStr.length); // remove url from css string til next loop
+			url = url.replace(/^[\s\t]*(['"]?)([\w()\/.\\'"-:#=&?]*)\1[\s\t]*?\)/,"$2"); // clean string
+			if(url.search(/(file|https?|ftps?):\/\//)==-1){
+				url = (new dojo.uri.Uri(URI,url).toString());
+			}
+			str += url;
+		};
+		return str+cssStr;
+	}
+
+	ds.getBackgroundColor = function(node) {
+		node = dojo.byId(node);
+		var color;
+		do{
+			color = ds.getStyle(node, "background-color");
+			// Safari doesn't say "transparent"
+			if(color.toLowerCase() == "rgba(0, 0, 0, 0)") { color = "transparent"; }
+			if(node == document.getElementsByTagName("body")[0]) { node = null; break; }
+			node = node.parentNode;
+		}while(node && dojo.lang.inArray(color, ["transparent", ""]));
+		if(color == "transparent"){
+			color = [255, 255, 255, 0];
+		}else{
+			color = dojo.graphics.color.extractRGB(color);
+		}
+		return color;
+	}
+
+	ds.getComputedStyle = function(node, cssSelector, inValue){
+		node = dojo.byId(node);
+		// cssSelector may actually be in camel case, so force selector version
+		var cssSelector = ds.toSelectorCase(cssSelector);
+		var property = ds.toCamelCase(cssSelector);
+		if(!node || !node.style){
+			return inValue;
+		}else if(document.defaultView){ // W3, gecko, KHTML
+			try{			
+				var cs = document.defaultView.getComputedStyle(node, "");
+				if (cs){ 
+					return cs.getPropertyValue(cssSelector);
+				} 
+			}catch(e){ // reports are that Safari can throw an exception above
+				if (node.style.getPropertyValue){ // W3
+					return node.style.getPropertyValue(cssSelector);
+				}else return inValue;
+			}
+		}else if(node.currentStyle){ // IE
+			return node.currentStyle[property];
+		}if(node.style.getPropertyValue){ // W3
+			return node.style.getPropertyValue(cssSelector);
+		}else{
+			return inValue;
+		}
+	}
+
+	/** 
+	 * Retrieve a property value from a node's style object.
+	 */
+	ds.getStyleProperty = function(node, cssSelector){
+		node = dojo.byId(node);
+		// FIXME: should we use node.style.getPropertyValue over style[property]?
+		// style[property] works in all (modern) browsers, getPropertyValue is W3 but not supported in IE
+		// FIXME: what about runtimeStyle?
+		return (node && node.style ? node.style[ds.toCamelCase(cssSelector)] : undefined);
+	}
+
+	/** 
+	 * Retrieve a property value from a node's style object.
+	 */
+	ds.getStyle = function(node, cssSelector){
+		var value = ds.getStyleProperty(node, cssSelector);
+		return (value ? value : ds.getComputedStyle(node, cssSelector));
+	}
+
+	ds.setStyle = function(node, cssSelector, value){
+		node = dojo.byId(node);
+		if(node && node.style){
+			var camelCased = ds.toCamelCase(cssSelector);
+			node.style[camelCased] = value;
+		}
+	}
+
+	ds.toCamelCase = function(selector) {
+		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;		
+	}
+
+	ds.toSelectorCase = function(selector) {
+		return selector.replace(/([A-Z])/g, "-$1" ).toLowerCase() ;
+	}
+
+	/* float between 0.0 (transparent) and 1.0 (opaque) */
+	ds.setOpacity = function setOpacity(node, opacity, dontFixOpacity) {
+		node = dojo.byId(node);
+		if(!dontFixOpacity){
+			if( opacity >= 1.0){
+				if(h.ie){
+					ds.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<tds.length; x++){
+					tds[x].style.filter = "Alpha(Opacity="+opacity*100+")";
+				}
+			}
+			node.style.filter = "Alpha(Opacity="+opacity*100+")";
+		}else if(h.moz){
+			node.style.opacity = opacity; // ffox 1.0 directly supports "opacity"
+			node.style.MozOpacity = opacity;
+		}else if(h.safari){
+			node.style.opacity = opacity; // 1.3 directly supports "opacity"
+			node.style.KhtmlOpacity = opacity;
+		}else{
+			node.style.opacity = opacity;
+		}
+	}
+		
+	ds.getOpacity = function getOpacity (node){
+		node = dojo.byId(node);
+		if(h.ie){
+			var opac = (node.filters && node.filters.alpha &&
+				typeof node.filters.alpha.opacity == "number"
+				? node.filters.alpha.opacity : 100) / 100;
+		}else{
+			var opac = node.style.opacity || node.style.MozOpacity ||
+				node.style.KhtmlOpacity || 1;
+		}
+		return opac >= 0.999999 ? 1.0 : 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 = ""; // FIXME: may get rid of other filter effects
+				}
+			} catch(e) {
+				/*
+				 * IE7 gives error if node.filters not set;
+				 * don't know why or how to workaround (other than this)
+				 */
+			}
+		}else if(h.moz){
+			ns.opacity = 1;
+			ns.MozOpacity = 1;
+		}else if(h.safari){
+			ns.opacity = 1;
+			ns.KhtmlOpacity = 1;
+		}else{
+			ns.opacity = 1;
+		}
+	}
+
+	/** 
+	* Set the given style attributes for the node. 
+	* Patch submitted by Wolfram Kriesing, 22/03/2006.
+	*
+	* Ie. dojo.style.setStyleAttributes(myNode, "position:absolute; left:10px; top:10px;") 
+	* This just makes it easier to set a style directly without the need to  
+	* override it completely (as node.setAttribute() would). 
+	* If there is a dojo-method for an attribute, like for "opacity" there 
+	* is setOpacity, the dojo method is called instead. 
+	* For example: dojo.style.setStyleAttributes(myNode, "opacity: .4"); 
+	*  
+	* Additionally all the dojo.style.set* methods can also be used. 
+	* Ie. when attributes contains "outer-height: 10;" it will call dojo.style.setOuterHeight("10"); 
+	* 
+	* @param object The node to set the style attributes for. 
+	* @param string Ie. "position:absolute; left:10px; top:10px;" 
+	*/ 
+	ds.setStyleAttributes = function(node, attributes) { 
+		var methodMap={ 
+			"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 splittedAttribs=attributes.replace(/(;)?\s*$/, "").split(";"); 
+		for(var i=0; i<splittedAttribs.length; i++){ 
+			var nameValue=splittedAttribs[i].split(":"); 
+			var name=nameValue[0].replace(/\s*$/, "").replace(/^\s*/, "").toLowerCase();
+			var value=nameValue[1].replace(/\s*$/, "").replace(/^\s*/, "");
+			if(dojo.lang.has(methodMap,name)) { 
+				methodMap[name](node,value); 
+			} else { 
+				node.style[dojo.style.toCamelCase(name)]=value; 
+			} 
+		} 
+	} 
+
+	ds._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()
+	ds.show = function(node){
+		node = dojo.byId(node);
+		if(ds.getStyleProperty(node, 'display')=='none'){
+			ds.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()
+	ds.hide = function(node){
+		node = dojo.byId(node);
+		if(typeof node["dojoDisplayCache"] == "undefined"){ // it could == '', so we cannot say !node.dojoDisplayCount
+			var d = ds.getStyleProperty(node, 'display')
+			if(d!='none'){
+				node.dojoDisplayCache = d;
+			}
+		}
+		ds.setStyle(node, 'display', 'none');
+	}
+
+	// setShowing() calls show() if showing is true, hide() otherwise
+	ds.setShowing = function(node, showing){
+		ds[(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
+	ds.isShowing = function(node){
+		return (ds.getStyleProperty(node, 'display') != 'none');
+	}
+
+	// Call setShowing() on node with the complement of isShowing(), then return the new value of isShowing()
+	ds.toggleShowing = function(node){
+		return ds._toggle(node, ds.isShowing, ds.setShowing);
+	}
+
+	// display is a CSS concept
+
+	// Simple mapping of tag names to display values
+	// FIXME: simplistic 
+	ds.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
+	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');
+		}
+	}
+
+	// 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
+	ds.setDisplay = function(node, display){
+		ds.setStyle(node, 'display', (dojo.lang.isString(display) ? display : (display ? ds.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
+	ds.isDisplayed = function(node){
+		return (ds.getComputedStyle(node, 'display') != 'none');
+	}
+
+	// Call setDisplay() on node with the complement of isDisplayed(), then
+	// return the new value of isDisplayed()
+	ds.toggleDisplay = function(node){
+		return ds._toggle(node, ds.isDisplayed, ds.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'.
+	ds.setVisibility = function(node, visibility){
+		ds.setStyle(node, 'visibility', (dojo.lang.isString(visibility) ? 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
+	ds.isVisible = function(node){
+		return (ds.getComputedStyle(node, 'visibility') != 'hidden');
+	}
+
+	// Call setVisibility() on node with the complement of isVisible(), then
+	// return the new value of isVisible()
+	ds.toggleVisibility = function(node){
+		return ds._toggle(node, ds.isVisible, ds.setVisibility);
+	}
+
+	// in: coordinate array [x,y,w,h] or dom node
+	// return: coordinate array
+	ds.toCoordinateArray = function(coords, includeScroll) {
+		if(dojo.lang.isArray(coords)){
+			// coords is already an array (of format [x,y,w,h]), just return it
+			while ( coords.length < 4 ) { coords.push(0); }
+			while ( coords.length > 4 ) { coords.pop(); }
+			var ret = coords;
+		} else {
+			// coords is an dom object (or dom object id); return it's coordinates
+			var node = dojo.byId(coords);
+			var pos = ds.getAbsolutePosition(node, includeScroll);
+			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;
+	};
+})();


Property changes on: trunk/PhotoOp/root/static/js/src/style.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/svg.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/svg.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/svg.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,279 @@
+/*
+	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.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<docFrag.childNodes.length; x++){
+		nodes.push(docFrag.childNodes.item(x).cloneNode(true));
+	}
+	return nodes;	// array
+}
+// vim:ts=4:noet:tw=0:


Property changes on: trunk/PhotoOp/root/static/js/src/svg.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/text/Builder.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/text/Builder.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/text/Builder.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,16 @@
+/*
+	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.text.Builder");
+dojo.require("dojo.string.Builder");
+
+dojo.deprecated("dojo.text.Builder", "use dojo.string.Builder instead", "0.4");
+
+dojo.text.Builder = dojo.string.Builder;


Property changes on: trunk/PhotoOp/root/static/js/src/text/Builder.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/text/String.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/text/String.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/text/String.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -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.text.String", "replaced by dojo.string", "0.4");
+dojo.require("dojo.string");
+
+dojo.text = dojo.string;
+dojo.provide("dojo.text.String");


Property changes on: trunk/PhotoOp/root/static/js/src/text/String.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/text/Text.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/text/Text.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/text/Text.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -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.text.Text", "replaced by dojo.string", "0.4");
+dojo.require("dojo.string");
+
+dojo.text = dojo.string;
+dojo.provide("dojo.text.Text");


Property changes on: trunk/PhotoOp/root/static/js/src/text/Text.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/text/__package__.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/text/__package__.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/text/__package__.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,17 @@
+/*
+	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.kwCompoundRequire({
+	common: [
+		"dojo.text.String",
+		"dojo.text.Builder"
+	]
+});
+dojo.provide("dojo.text.*");


Property changes on: trunk/PhotoOp/root/static/js/src/text/__package__.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/text/textDirectory.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/text/textDirectory.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/text/textDirectory.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,81 @@
+/*
+	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.text.textDirectory");
+dojo.provide("dojo.text.textDirectory.Property");
+dojo.provide("dojo.text.textDirectory.tokenise");
+dojo.require("dojo.string");
+
+/* adapted from Paul Sowden's iCalendar work */
+
+dojo.textDirectoryTokeniser = function () {}
+
+/*
+ * This class parses a single line from a text/directory file
+ * and returns an object with four named values; name, group, params
+ * and value. name, group and value are strings containing the original
+ * tokens unaltered and values is an array containing name/value pairs
+ * or a single name token packed into arrays.
+ */
+dojo.textDirectoryTokeniser.Property = function (line) {
+	// split into name/value pair
+	var left = dojo.string.trim(line.substring(0, line.indexOf(':')));
+	var right = dojo.string.trim(line.substr(line.indexOf(':') + 1));
+
+	// seperate name and paramters	
+	var parameters = dojo.string.splitEscaped(left,';');
+	this.name = parameters[0]
+	parameters.splice(0, 1);
+
+	// parse paramters
+	this.params = [];
+	for (var i = 0; i < parameters.length; i++) {
+		var arr = parameters[i].split("=");
+		var key = dojo.string.trim(arr[0].toUpperCase());
+		
+		if (arr.length == 1) { this.params.push([key]); continue; }
+		
+		var values = dojo.string.splitEscaped(arr[1],',');
+		for (var j = 0; j < values.length; j++) {
+			if (dojo.string.trim(values[j]) != '') {
+				this.params.push([key, dojo.string.trim(values[j])]);
+			}
+		}
+	}
+
+	// seperate group
+	if (this.name.indexOf('.') > 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;
+}


Property changes on: trunk/PhotoOp/root/static/js/src/text/textDirectory.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/undo/Manager.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/undo/Manager.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/undo/Manager.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -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.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
+			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;
+		}
+	}
+});


Property changes on: trunk/PhotoOp/root/static/js/src/undo/Manager.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/undo/__package__.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/undo/__package__.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/undo/__package__.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -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.undo.Manager");
+dojo.provide("dojo.undo.*");


Property changes on: trunk/PhotoOp/root/static/js/src/undo/__package__.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/undo/browser.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/undo/browser.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/undo/browser.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,279 @@
+/*
+	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.undo.browser");
+dojo.require("dojo.io");
+
+try{
+	if((!djConfig["preventBackButtonFix"])&&(!dojo.hostenv.post_load_)){
+		document.write("<iframe style='border: 0px; width: 1px; height: 1px; position: absolute; bottom: 0px; right: 0px; visibility: visible;' name='djhistory' id='djhistory' src='"+(dojo.hostenv.getBaseScriptUri()+'iframe_history.html')+"'></iframe>");
+	}
+}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");
+			(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 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];
+		}
+	}
+}


Property changes on: trunk/PhotoOp/root/static/js/src/undo/browser.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/uri/Uri.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/uri/Uri.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/uri/Uri.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,108 @@
+/*
+	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.uri.Uri");
+
+dojo.uri = new function() {
+	this.joinPath = function() {
+		// DEPRECATED: use the dojo.uri.Uri object instead
+		var arr = [];
+		for(var i = 0; i < arguments.length; i++) { arr.push(arguments[i]); }
+		return arr.join("/").replace(/\/{2,}/g, "/").replace(/((https*|ftps*):)/i, "$1/");
+	}
+	
+	this.dojoUri = function (uri) {
+		// returns a Uri object resolved relative to the dojo root
+		return new dojo.uri.Uri(dojo.hostenv.getBaseScriptUri(), 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; }
+	}
+};


Property changes on: trunk/PhotoOp/root/static/js/src/uri/Uri.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/uri/__package__.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/uri/__package__.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/uri/__package__.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,14 @@
+/*
+	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.kwCompoundRequire({
+	common: ["dojo.uri.Uri", false, false]
+});
+dojo.provide("dojo.uri.*");


Property changes on: trunk/PhotoOp/root/static/js/src/uri/__package__.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/uuid/LightweightGenerator.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/uuid/LightweightGenerator.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/uuid/LightweightGenerator.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,82 @@
+/*
+	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.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:
+ * <pre>
+ *   var string = dojo.uuid.LightweightGenerator.generate();
+ *   var string = dojo.uuid.LightweightGenerator.generate(String);
+ *   var uuid   = dojo.uuid.LightweightGenerator.generate(dojo.uuid.Uuid);
+ * </pre>
+ *
+ * @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;
+	};
+}();


Property changes on: trunk/PhotoOp/root/static/js/src/uuid/LightweightGenerator.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/uuid/NameBasedGenerator.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/uuid/NameBasedGenerator.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/uuid/NameBasedGenerator.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,43 @@
+/*
+	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.uuid.NameBasedGenerator");
+
+dojo.uuid.NameBasedGenerator = new function() {
+
+/**
+ * This function generates name-based UUIDs, meaning "version 3" 
+ * and "version 5" UUIDs.
+ *
+ * Examples:
+ * <pre>
+ *   var string = dojo.uuid.NameBasedGenerator.generate();
+ *   var string = dojo.uuid.NameBasedGenerator.generate(String);
+ *   var uuid   = dojo.uuid.NameBasedGenerator.generate(dojo.uuid.Uuid);
+ * </pre>
+ *
+ * @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


Property changes on: trunk/PhotoOp/root/static/js/src/uuid/NameBasedGenerator.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/uuid/NilGenerator.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/uuid/NilGenerator.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/uuid/NilGenerator.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,38 @@
+/*
+	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.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:
+ * <pre>
+ *   var string = dojo.uuid.NilGenerator.generate();
+ *   var string = dojo.uuid.NilGenerator.generate(String);
+ *   var uuid   = dojo.uuid.NilGenerator.generate(dojo.uuid.Uuid);
+ * </pre>
+ *
+ * @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


Property changes on: trunk/PhotoOp/root/static/js/src/uuid/NilGenerator.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/uuid/RandomGenerator.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/uuid/RandomGenerator.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/uuid/RandomGenerator.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,44 @@
+/*
+	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.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:
+ * <pre>
+ *   var string = dojo.uuid.RandomGenerator.generate();
+ *   var string = dojo.uuid.RandomGenerator.generate(String);
+ *   var uuid   = dojo.uuid.RandomGenerator.generate(dojo.uuid.Uuid);
+ * </pre>
+ *
+ * @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;
+	};
+}();


Property changes on: trunk/PhotoOp/root/static/js/src/uuid/RandomGenerator.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/uuid/TimeBasedGenerator.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/uuid/TimeBasedGenerator.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/uuid/TimeBasedGenerator.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,391 @@
+/*
+	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.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:
+ * <pre>
+ *   result = _padWithLeadingZeros("abc", 6);
+ *   dojo.lang.assert(result == "000abc");
+ * </pre>
+ *
+ * @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"]);
+		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:
+ * <pre>
+ *   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"
+ * </pre>
+ *
+ * @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"]);
+			} 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"]);
+		}
+		
+		var uuidString = _generateUuidString(nodeString);
+		var returnValue;
+		if (returnType && (returnType != String)) {
+			returnValue = new returnType(uuidString);
+		} else {
+			returnValue = uuidString;
+		}
+		return returnValue;
+	};
+}();


Property changes on: trunk/PhotoOp/root/static/js/src/uuid/TimeBasedGenerator.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/uuid/Uuid.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/uuid/Uuid.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/uuid/Uuid.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,423 @@
+/*
+	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.uuid.Uuid");
+dojo.require("dojo.lang.*");
+
+// -------------------------------------------------------------------
+// Constructor
+// -------------------------------------------------------------------
+/**
+ * The Uuid class offers methods for inspecting existing UUIDs.
+ *
+ * Examples:
+ * <pre>
+ *   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));
+ * </pre>
+ *
+ * @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:
+ * <pre>
+ *   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);
+ * </pre>
+ *
+ * @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:
+ * <pre>
+ *   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
+ * </pre>
+ *
+ * @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:
+ * <pre>
+ *   var uuid = new dojo.uuid.Uuid("3b12f1df-5232-4804-897e-917bf397618a");
+ *   var variant = uuid.getVariant();
+ *   dojo.lang.assert(variant == dojo.uuid.Uuid.Variant.DCE);
+ * </pre>
+ *
+ * @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:
+ * <pre>
+ *   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);
+ * </pre>
+ *
+ * @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:
+ * <pre>
+ *   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"
+ * </pre>
+ *
+ * @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;
+	}
+};


Property changes on: trunk/PhotoOp/root/static/js/src/uuid/Uuid.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/uuid/__package__.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/uuid/__package__.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/uuid/__package__.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,22 @@
+/*
+	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.kwCompoundRequire({
+	common: [
+		"dojo.uuid.Uuid",
+		"dojo.uuid.LightweightGenerator",
+		"dojo.uuid.RandomGenerator",
+		"dojo.uuid.TimeBasedGenerator",
+		"dojo.uuid.NameBasedGenerator",
+		"dojo.uuid.NilGenerator"
+	]
+});
+dojo.provide("dojo.uuid.*");
+


Property changes on: trunk/PhotoOp/root/static/js/src/uuid/__package__.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/validate/__package__.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/validate/__package__.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/validate/__package__.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,21 @@
+/*
+	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.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.*");


Property changes on: trunk/PhotoOp/root/static/js/src/validate/__package__.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/validate/check.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/validate/check.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/validate/check.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,221 @@
+/*
+	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.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])){
+				// case 2: constraint value is array, first elem is function,
+				// tail is parameters
+				var isValidSomething = profile.constraints[name][0];
+				var params = profile.constraints[name].slice(1);
+				params.unshift(elem.value);
+				if(typeof isValidSomething != "undefined"){
+					isValid = isValidSomething.apply(null, params);
+				}else{
+					isValid = false; 
+				}
+			}
+
+			if(!isValid){	
+				invalid[invalid.length] = elem.name;
+			}
+		}
+	}
+
+	// Find unequal confirm fields and report them as Invalid.
+	if(dojo.lang.isObject(profile.confirm)){
+		for(name in profile.confirm){
+			var elem = form[name];	// the confirm element
+			var target = form[profile.confirm[name]];
+			if ( (elem.type != "text" && elem.type != "textarea" && elem.type != "password") 
+				||(target.type != elem.type)
+				||(target.value == elem.value)	// it's valid
+				||(results.isInvalid(elem.name))// already listed as invalid
+				||(/^\s*$/.test(target.value))	)	// skip if blank - only confirm if target has a value
+			{
+				continue; 
+			}	
+			invalid[invalid.length] = elem.name;
+		}
+	}
+
+	return results;
+}


Property changes on: trunk/PhotoOp/root/static/js/src/validate/check.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/validate/common.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/validate/common.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/validate/common.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,226 @@
+/*
+	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.validate.common");
+dojo.require("dojo.validate");
+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.cents  The two decimal places for 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) {
+	// 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.
+
+*/


Property changes on: trunk/PhotoOp/root/static/js/src/validate/common.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/validate/datetime.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/validate/datetime.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/validate/datetime.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,168 @@
+/*
+	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.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;
+}


Property changes on: trunk/PhotoOp/root/static/js/src/validate/datetime.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/validate/de.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/validate/de.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/validate/de.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,30 @@
+/*
+	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.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);
+}
+
+


Property changes on: trunk/PhotoOp/root/static/js/src/validate/de.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/validate/jp.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/validate/jp.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/validate/jp.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,28 @@
+/*
+	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.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);
+}
+
+


Property changes on: trunk/PhotoOp/root/static/js/src/validate/jp.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/validate/us.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/validate/us.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/validate/us.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,94 @@
+/*
+	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.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);
+}


Property changes on: trunk/PhotoOp/root/static/js/src/validate/us.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/validate/web.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/validate/web.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/validate/web.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,105 @@
+/*
+	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.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 <mailto:foo at yahoo.com>.  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 [];
+}
+
+


Property changes on: trunk/PhotoOp/root/static/js/src/validate/web.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/validate.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/validate.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/validate.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -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.provide("dojo.validate");
+dojo.require("dojo.validate.common");


Property changes on: trunk/PhotoOp/root/static/js/src/validate.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/AccordionContainer.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/AccordionContainer.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/AccordionContainer.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,71 @@
+/*
+	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.AccordionContainer");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.AccordionPane");
+
+dojo.widget.defineWidget(
+	"dojo.widget.AccordionContainer",
+	dojo.widget.HtmlWidget,
+	{
+		widgetType: "AccordionContainer",
+		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
+});
+


Property changes on: trunk/PhotoOp/root/static/js/src/widget/AccordionContainer.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/AccordionPane.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/AccordionPane.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/AccordionPane.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -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.provide("dojo.widget.AccordionPane");
+dojo.requireAfterIf("html", "dojo.widget.html.AccordionPane");


Property changes on: trunk/PhotoOp/root/static/js/src/widget/AccordionPane.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/AnimatedPng.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/AnimatedPng.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/AnimatedPng.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,83 @@
+/*
+	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.AnimatedPng");
+dojo.provide("dojo.widget.html.AnimatedPng");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.HtmlWidget");
+
+
+dojo.widget.defineWidget(
+	"dojo.widget.html.AnimatedPng",
+	dojo.widget.HtmlWidget,
+	{
+
+		widgetType: "AnimatedPng",
+		isContainer: false,
+
+		domNode: null,
+		width: 0,
+		height: 0,
+		aniSrc: '',
+		interval: 100,
+
+		cellWidth: 0,
+		cellHeight: 0,
+		aniCols: 1,
+		aniRows: 1,
+		aniCells: 1,
+
+		blankSrc: dojo.uri.dojoUri("src/widget/templates/images/blank.gif"),
+
+		templateString: '<img class="dojoAnimatedPng" />',
+
+		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';
+		}
+	}
+);


Property changes on: trunk/PhotoOp/root/static/js/src/widget/AnimatedPng.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/Button.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/Button.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/Button.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,315 @@
+/*
+	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.Button");
+dojo.provide("dojo.widget.html.Button");
+
+dojo.require("dojo.lang.extras");
+dojo.require("dojo.html");
+dojo.require("dojo.style");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.HtmlWidget");
+
+dojo.widget.defineWidget(
+	"dojo.widget.html.Button",
+	dojo.widget.HtmlWidget,
+	{
+		widgetType: "Button",
+		isContainer: true,
+	
+		// Constructor arguments
+		caption: "",
+		disabled: false,
+	
+		templatePath: dojo.uri.dojoUri("src/widget/templates/HtmlButtonTemplate.html"),
+		templateCssPath: dojo.uri.dojoUri("src/widget/templates/HtmlButtonTemplate.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.dom.insertBefore(placeHolder, this.domNode);
+			}
+			dojo.html.body().appendChild(this.domNode);
+			
+			this.sizeMyselfHelper();
+			
+			// Put this.domNode back where it was originally
+			if(placeHolder){
+				dojo.dom.insertBefore(this.domNode, placeHolder);
+				dojo.dom.removeNode(placeHolder);
+			}
+		},
+
+		sizeMyselfHelper: function(){
+			this.height = dojo.style.getOuterHeight(this.containerNode);
+			this.containerWidth = dojo.style.getOuterWidth(this.containerNode);
+			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; }
+			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.style.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.html.DropDownButton",
+	dojo.widget.html.Button,
+	{
+		widgetType: "DropDownButton",
+	
+		menuId: "",
+
+		arrow: null,
+	
+		downArrow: "src/widget/templates/images/whiteDownArrow.gif",
+		disabledDownArrow: "src/widget/templates/images/whiteDownArrow.gif",
+	
+		fillInTemplate: function(args, frag){
+			dojo.widget.html.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.html.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.html.ComboButton",
+	dojo.widget.html.Button,
+	{
+		widgetType: "ComboButton",
+	
+		menuId: "",
+	
+		templatePath: dojo.uri.dojoUri("src/widget/templates/HtmlComboButtonTemplate.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){
+			this.height = dojo.style.getOuterHeight(this.containerNode);
+			this.containerWidth = dojo.style.getOuterWidth(this.containerNode);
+			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");
+		}
+	});
\ No newline at end of file


Property changes on: trunk/PhotoOp/root/static/js/src/widget/Button.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/Button2.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/Button2.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/Button2.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -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" });


Property changes on: trunk/PhotoOp/root/static/js/src/widget/Button2.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/Chart.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/Chart.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/Chart.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,199 @@
+/*
+	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.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");
+
+dojo.widget.Chart = function(){
+	dojo.widget.Widget.call(this);
+	this.widgetType = "Chart";
+	this.isContainer = false;
+	this.series = [];
+	// FIXME: why is this a mixin method?
+	this.assignColors = function(){
+		var hue=30;
+		var sat=120;
+		var lum=120;
+		var steps = Math.round(330/this.series.length);
+
+		for(var i=0; i<this.series.length; i++){
+			var c=dojo.graphics.color.hsl2rgb(hue,sat,lum);
+			if(!this.series[i].color){
+				this.series[i].color = dojo.graphics.color.rgb2hex(c[0],c[1],c[2]);
+			}
+			hue += steps;
+		}
+	};
+}
+dojo.inherits(dojo.widget.Chart, dojo.widget.Widget);
+
+dojo.widget.Chart.PlotTypes = {
+	Bar:"bar",
+	Line:"line",
+	Scatter:"scatter",
+	Bubble:"bubble"
+};
+
+/*
+ *	Every chart has a set of data series; this is the series.  Note that each
+ *	member of value is an object and in the minimum has 2 properties: .x and
+ *	.value.
+ */
+dojo.widget.Chart.DataSeries = function(key, label, plotType, color){
+	// FIXME: why the hell are plot types specified neumerically? What is this? C?
+	this.id = "DataSeries"+dojo.widget.Chart.DataSeries.count++;
+	this.key = key;
+	this.label = label||this.id;
+	this.plotType = plotType||0;
+	this.color = color;
+	this.values = [];
+};
+
+dojo.lang.extend(dojo.widget.Chart.DataSeries, {
+	add: function(v){
+		if(v.x==null||v.value==null){
+			dojo.raise("dojo.widget.Chart.DataSeries.add: v must have both an 'x' and 'value' property.");
+		}
+		this.values.push(v);
+	},
+
+	clear: function(){
+		this.values=[];
+	},
+
+	createRange: function(len){
+		var idx = this.values.length-1;
+		var length = (len||this.values.length);
+		return { "index": idx, "length": length, "start":Math.max(idx-length,0) };
+	},
+
+	//	trend values
+	getMean: 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;
+	},
+
+	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; j<a.length&&!b; j++){
+					if (n==a[j]) b=true; 
+				}
+				if(!b){ a.push(n); }
+			}
+		}
+		a.sort();
+		if(a.length>0){ 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(m<o[p]){ m=o[p]; ret=p; }
+		}
+		return parseFloat(ret);
+	}
+});
+
+dojo.requireIf(dojo.render.svg.support.builtin, "dojo.widget.svg.Chart");
+dojo.requireIf(dojo.render.html.ie, "dojo.widget.vml.Chart");


Property changes on: trunk/PhotoOp/root/static/js/src/widget/Chart.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/Checkbox.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/Checkbox.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/Checkbox.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,13 @@
+/*
+	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.Checkbox");
+
+dojo.requireAfterIf("html", "dojo.widget.html.Checkbox");


Property changes on: trunk/PhotoOp/root/static/js/src/widget/Checkbox.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/CiviCrmDatePicker.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/CiviCrmDatePicker.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/CiviCrmDatePicker.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,118 @@
+/*
+	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.CiviCrmDatePicker");
+dojo.provide("dojo.widget.HtmlCiviCrmDatePicker");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.event.*");
+dojo.require("dojo.widget.DatePicker");
+dojo.require("dojo.widget.html.DatePicker");
+dojo.require("dojo.widget.html.TimePicker");
+dojo.require("dojo.html");
+
+dojo.widget.HtmlCiviCrmDatePicker = function(){
+	this.widgetType = "CiviCrmDatePicker";
+	this.idPrefix = "scheduled_date_time";
+	this.mode = "datetime"; // can also be date or time
+
+	this.datePicker = null;
+	this.timePicker = null;
+
+	// html nodes
+	this.dateHolderTd = null;
+	this.timeHolderTd = null;
+	this.formItemsTd = null;
+	this.formItemsTr = null;
+
+	this.monthSelect = null;
+	this.daySelect = null;
+	this.yearSelect = null;
+	this.hourSelect = null;
+	this.minSelect = null;
+	this.apSelect = null;
+
+	this.templatePath = dojo.uri.dojoUri("src/widget/templates/HtmlCiviCrmDatePicker.html");
+
+	this.modeFormats = {
+		date: "MdY",
+		time: "hiA"
+	};
+
+	this.formatMappings = {
+		"M": "monthSelect",
+		"d": "daySelect",
+		"Y": "yearSelect",
+		"h": "hourSelect",
+		"i": "minSelect",
+		"A": "apSelect"
+	};
+
+	this.setDateSelects = function(){
+		var dateObj = this.datePicker.date;
+		this.monthSelect.value = new String(dateObj.getMonth()+1);
+		this.daySelect.value = new String(dateObj.getDate());
+		this.yearSelect.value = new String(dateObj.getFullYear());
+	}
+
+	this.setTimeSelects = function(){
+		var st = this.timePicker.selectedTime;
+		this.hourSelect.value = new String(st.hour);
+		this.minSelect.value = new String(st.minute);
+		this.apSelect.value = st.amPm.toUpperCase();
+	}
+
+	this.fillInTemplate = function(args, frag){
+		var nr = frag["dojo:"+this.widgetType.toLowerCase()]["nodeRef"];
+		var sref = {};
+		while(nr.firstChild){
+			if(nr.firstChild.name){
+				sref[nr.firstChild.name] = nr.firstChild;
+			}
+			this.formItemsTd.appendChild(nr.firstChild);
+		}
+
+		if(this.mode.indexOf("date") != -1){
+			this.datePicker = dojo.widget.createWidget("DatePicker", {}, this.dateHolderTd);
+			dojo.event.connect(	this.datePicker, "onSetDate", 
+								this, "setDateSelects");
+
+			var mfd = this.modeFormats.date;
+			for(var x=0; x<mfd.length; x++){
+				this[this.formatMappings[mfd[x]]] = sref[this.idPrefix+"["+mfd[x]+"]"];
+			}
+		}
+		if(this.mode.indexOf("time") != -1){
+			this.timePicker = dojo.widget.createWidget("TimePicker", {}, this.timeHolderTd);
+			dojo.event.connect(	this.timePicker, "onSetTime", 
+								this, "setTimeSelects");
+			var mfd = this.modeFormats.time;
+			for(var x=0; x<mfd.length; x++){
+				this[this.formatMappings[mfd[x]]] = sref[this.idPrefix+"["+mfd[x]+"]"];
+			}
+		}
+	}
+
+	this.unhide = function(){
+		this.formItemsTr.style.display = "";
+	}
+
+	this.postCreate = function(){
+		dojo.event.kwConnect({
+			type: "before", 
+			srcObj: dojo.html.getParentByType(this.domNode, "form"),
+			srcFunc: "onsubmit", 
+			targetObj: this,
+			targetFunc: "unhide"
+		});
+	}
+}
+dojo.inherits(dojo.widget.HtmlCiviCrmDatePicker, dojo.widget.HtmlWidget);
+dojo.widget.tags.addParseTreeHandler("dojo:civicrmdatepicker");
+


Property changes on: trunk/PhotoOp/root/static/js/src/widget/CiviCrmDatePicker.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/ColorPalette.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/ColorPalette.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/ColorPalette.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,186 @@
+/*
+	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.ColorPalette");
+dojo.provide("dojo.widget.html.ColorPalette");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.Toolbar");
+dojo.require("dojo.html");
+
+dojo.widget.tags.addParseTreeHandler("dojo:ToolbarColorDialog");
+
+dojo.widget.html.ToolbarColorDialog = function(){
+	dojo.widget.html.ToolbarDialog.call(this);
+	
+	/*
+	FIXME: 	why the fuck did anyone ever think this kind of expensive iteration
+			was a good idea?
+
+	for (var method in this.constructor.prototype) {
+		this[method] = this.constructor.prototype[method];
+	}
+	*/
+}
+
+dojo.inherits(dojo.widget.html.ToolbarColorDialog, dojo.widget.html.ToolbarDialog);
+
+dojo.lang.extend(dojo.widget.html.ToolbarColorDialog, {
+
+	widgetType: "ToolbarColorDialog",
+
+	palette: "7x10",
+
+	fillInTemplate: function (args, frag) {
+		dojo.widget.html.ToolbarColorDialog.superclass.fillInTemplate.call(this, args, frag);
+		this.dialog = dojo.widget.createWidget("ColorPalette", {palette: this.palette});
+		this.dialog.domNode.style.position = "absolute";
+
+		dojo.event.connect(this.dialog, "onColorSelect", this, "_setValue");
+	},
+
+	_setValue: function(color) {
+		this._value = color;
+		this._fireEvent("onSetValue", color);
+	},
+	
+	showDialog: function (e) {
+		dojo.widget.html.ToolbarColorDialog.superclass.showDialog.call(this, e);
+		var x = dojo.html.getAbsoluteX(this.domNode);
+		var y = dojo.html.getAbsoluteY(this.domNode) + dojo.html.getInnerHeight(this.domNode);
+		this.dialog.showAt(x, y);
+	},
+	
+	hideDialog: function (e) {
+		dojo.widget.html.ToolbarColorDialog.superclass.hideDialog.call(this, e);
+		this.dialog.hide();
+	}
+});
+
+
+
+dojo.widget.tags.addParseTreeHandler("dojo:colorpalette");
+
+dojo.widget.html.ColorPalette = function () {
+	dojo.widget.HtmlWidget.call(this);
+}
+
+dojo.inherits(dojo.widget.html.ColorPalette, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.html.ColorPalette, {
+
+	widgetType: "colorpalette",
+	
+	palette: "7x10",
+
+	bgIframe: null,
+	
+	palettes: {
+		"7x10": [["fff", "fcc", "fc9", "ff9", "ffc", "9f9", "9ff", "cff", "ccf", "fcf"],
+			["ccc", "f66", "f96", "ff6", "ff3", "6f9", "3ff", "6ff", "99f", "f9f"],
+			["c0c0c0", "f00", "f90", "fc6", "ff0", "3f3", "6cc", "3cf", "66c", "c6c"],
+			["999", "c00", "f60", "fc3", "fc0", "3c0", "0cc", "36f", "63f", "c3c"],
+			["666", "900", "c60", "c93", "990", "090", "399", "33f", "60c", "939"],
+			["333", "600", "930", "963", "660", "060", "366", "009", "339", "636"],
+			["000", "300", "630", "633", "330", "030", "033", "006", "309", "303"]],
+	
+		"3x4": [["ffffff"/*white*/, "00ff00"/*lime*/, "008000"/*green*/, "0000ff"/*blue*/],
+			["c0c0c0"/*silver*/, "ffff00"/*yellow*/, "ff00ff"/*fuchsia*/, "000080"/*navy*/],
+			["808080"/*gray*/, "ff0000"/*red*/, "800080"/*purple*/, "000000"/*black*/]]
+			//["00ffff"/*aqua*/, "808000"/*olive*/, "800000"/*maroon*/, "008080"/*teal*/]];
+	},
+
+	buildRendering: function () {
+		
+		this.domNode = document.createElement("table");
+		dojo.html.disableSelection(this.domNode);
+		dojo.event.connect(this.domNode, "onmousedown", function (e) {
+			e.preventDefault();
+		});
+		with (this.domNode) { // set the table's properties
+			cellPadding = "0"; cellSpacing = "1"; border = "1";
+			style.backgroundColor = "white"; //style.position = "absolute";
+		}
+		var tbody = document.createElement("tbody");
+		this.domNode.appendChild(tbody);
+		var colors = this.palettes[this.palette];
+		for (var i = 0; i < colors.length; i++) {
+			var tr = document.createElement("tr");
+			for (var j = 0; j < colors[i].length; j++) {
+				if (colors[i][j].length == 3) {
+					colors[i][j] = colors[i][j].replace(/(.)(.)(.)/, "$1$1$2$2$3$3");
+				}
+	
+				var td = document.createElement("td");
+				with (td.style) {
+					backgroundColor = "#" + colors[i][j];
+					border = "1px solid gray";
+					width = height = "15px";
+					fontSize = "1px";
+				}
+	
+				td.color = "#" + colors[i][j];
+	
+				td.onmouseover = function (e) { this.style.borderColor = "white"; }
+				td.onmouseout = function (e) { this.style.borderColor = "gray"; }
+				dojo.event.connect(td, "onmousedown", this, "click");
+	
+				td.innerHTML = "&nbsp;";
+				tr.appendChild(td);
+			}
+			tbody.appendChild(tr);
+		}
+
+		if(dojo.render.html.ie){
+			this.bgIframe = document.createElement("<iframe frameborder='0' src='javascript:void(0);'>");
+			with(this.bgIframe.style){
+				position = "absolute";
+				left = top = "0px";
+				display = "none";
+			}
+			document.body.appendChild(this.bgIframe);
+			dojo.style.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;
+		}
+		document.body.appendChild(this.domNode);
+		if(this.bgIframe){
+			with(this.bgIframe.style){
+				display = "block";
+				top = y + "px";
+				left = x + "px";
+				zIndex = 998;
+				width = dojo.html.getOuterWidth(this.domNode) + "px";
+				height = dojo.html.getOuterHeight(this.domNode) + "px";
+			}
+
+		}
+	}
+
+});


Property changes on: trunk/PhotoOp/root/static/js/src/widget/ColorPalette.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/ComboBox.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/ComboBox.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/ComboBox.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,236 @@
+/*
+	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.ComboBox");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.event.*");
+
+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.paramString(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 = 500;
+	this.searchLimit = 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 <option> elements
+			if((node)&&(node.nodeName.toLowerCase() == "select")){
+				// NOTE: we're not handling <optgroup> here yet
+				var opts = node.getElementsByTagName("option");
+				var ol = opts.length;
+				var data = [];
+				for(var x=0; x<ol; x++){
+					var keyValArr = [new String(opts[x].innerHTML), new String(opts[x].value)];
+					data.push(keyValArr);
+					if(opts[x].selected){ 
+						cbox.setAllValues(keyValArr[0], keyValArr[1]);
+					}
+				}
+				this.setData(data);
+			}
+		}
+	}
+
+	this.getData = function(url){
+		dojo.io.bind({
+			url: url,
+			load: dojo.lang.hitch(this, function(type, data, evt){ 
+				if(!dojo.lang.isArray(data)){
+					var arrData = [];
+					for(var key in data){
+						arrData.push([data[key], key]);
+					}
+					data = arrData;
+				}
+				this.setData(data);
+			}),
+			mimetype: "text/json"
+		});
+	}
+
+	this.startSearch = function(searchStr, type, ignoreLimit){
+		// FIXME: need to add timeout handling here!!
+		this._preformSearch(searchStr, type, ignoreLimit);
+	}
+
+	this._preformSearch = function(searchStr, type, ignoreLimit){
+		//
+		//	NOTE: this search is LINEAR, which means that it exhibits perhaps
+		//	the worst possible speed characteristics of any search type. It's
+		//	written this way to outline the responsibilities and interfaces for
+		//	a search.
+		//
+		var st = type||this.searchType;
+		// FIXME: this is just an example search, which means that we implement
+		// only a linear search without any of the attendant (useful!) optimizations
+		var ret = [];
+		if(!this.caseSensitive){
+			searchStr = searchStr.toLowerCase();
+		}
+		for(var x=0; x<this.data.length; x++){
+			if((!ignoreLimit)&&(ret.length >= 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.declare(
+	"dojo.widget.ComboBox",
+	null,
+	{
+		widgetType: "ComboBox",
+		isContainer: false,
+	
+		forceValidOption: false,
+		searchType: "stringstart",
+		dataProvider: null,
+	
+		startSearch: function(searchString){},
+		openResultList: function(results){},
+		clearResultList: function(){},
+		hideResultList: function(){},
+		selectNextResult: function(){},
+		selectPrevResult: function(){},
+		setSelectedResult: function(){}
+	}
+);
+
+// render-specific includes
+dojo.requireAfterIf("html", "dojo.widget.html.ComboBox");
+


Property changes on: trunk/PhotoOp/root/static/js/src/widget/ComboBox.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/ContentPane.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/ContentPane.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/ContentPane.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,16 @@
+/*
+	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 widget doesn't do anything; is basically the same as <div>.
+// 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.requireAfterIf("html", "dojo.widget.html.ContentPane");


Property changes on: trunk/PhotoOp/root/static/js/src/widget/ContentPane.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/ContextMenu.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/ContextMenu.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/ContextMenu.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -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");


Property changes on: trunk/PhotoOp/root/static/js/src/widget/ContextMenu.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/DatePicker.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/DatePicker.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/DatePicker.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,61 @@
+/*
+	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.DatePicker");
+dojo.provide("dojo.widget.DatePicker.util");
+dojo.require("dojo.widget.DomWidget");
+dojo.require("dojo.date");
+
+// NOTE: this function is only used as mixin (never as a constructor)
+dojo.widget.DatePicker = function() {
+	// the following aliases prevent breaking people using 0.2.x
+	this.months = dojo.date.months,
+	this.weekdays = dojo.date.days,
+	this.toRfcDate = dojo.widget.DatePicker.util.toRfcDate,
+	this.fromRfcDate = dojo.widget.DatePicker.util.fromRfcDate,
+	this.initFirstSaturday = dojo.widget.DatePicker.util.initFirstSaturday
+};
+
+dojo.requireAfterIf("html", "dojo.widget.html.DatePicker");
+
+dojo.widget.DatePicker.util = new function() {
+	this.months = dojo.date.months;
+	this.weekdays = dojo.date.days;
+	
+	this.toRfcDate = function(jsDate) {
+		if(!jsDate) {
+			var 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()};
+	}
+}


Property changes on: trunk/PhotoOp/root/static/js/src/widget/DatePicker.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/DebugConsole.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/DebugConsole.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/DebugConsole.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,22 @@
+/*
+	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.DebugConsole");
+dojo.require("dojo.widget.Widget");
+
+dojo.widget.DebugConsole= function(){
+	dojo.widget.Widget.call(this);
+
+	this.widgetType = "DebugConsole";
+	this.isContainer = true;
+}
+dojo.inherits(dojo.widget.DebugConsole, dojo.widget.Widget);
+dojo.widget.tags.addParseTreeHandler("dojo:debugconsole");
+dojo.requireAfterIf("html", "dojo.widget.html.DebugConsole");


Property changes on: trunk/PhotoOp/root/static/js/src/widget/DebugConsole.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/DemoEngine.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/DemoEngine.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/DemoEngine.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -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.provide("dojo.widget.DemoEngine");
+dojo.requireAfterIf("html", "dojo.widget.html.DemoEngine");

Added: trunk/PhotoOp/root/static/js/src/widget/Dialog.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/Dialog.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/Dialog.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,269 @@
+/*
+	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.Dialog");
+dojo.provide("dojo.widget.html.Dialog");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.ContentPane");
+dojo.require("dojo.event.*");
+dojo.require("dojo.graphics.color");
+dojo.require("dojo.html");
+
+dojo.widget.defineWidget(
+	"dojo.widget.html.Dialog",
+	dojo.widget.html.ContentPane,
+	{
+		templatePath: dojo.uri.dojoUri("src/widget/templates/HtmlDialog.html"),
+		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: "",
+
+		bg: null,
+		bgColor: "black",
+		bgOpacity: 0.4,
+		followScroll: true,
+		_fromTrap: false,
+		anim: null,
+		blockDuration: 0,
+		lifetime: 0,
+
+		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);
+		},
+
+		postCreate: function(args, frag, parentComp) {
+			with(this.domNode.style) {
+				position = "absolute";
+				zIndex = 999;
+				display = "none";
+				overflow = "visible";
+			}
+			var b = document.body;
+			b.appendChild(this.domNode);
+
+			this.bg = document.createElement("div");
+			this.bg.className = "dialogUnderlay";
+			with(this.bg.style) {
+				position = "absolute";
+				left = top = "0px";
+				zIndex = 998;
+				display = "none";
+			}
+			this.setBackgroundColor(this.bgColor);
+			b.appendChild(this.bg);
+
+			this.bgIframe = new dojo.html.BackgroundIframe(this.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.bg.style.backgroundColor = color.toString();
+			return this.bgColor = color;
+		},
+		
+		setBackgroundOpacity: function(op) {
+			if(arguments.length == 0) { op = this.bgOpacity; }
+			dojo.style.setOpacity(this.bg, op);
+			try {
+				this.bgOpacity = dojo.style.getOpacity(this.bg);
+			} catch (e) {
+				this.bgOpacity = op;
+			}
+			return this.bgOpacity;
+		},
+
+		sizeBackground: function() {
+			if(this.bgOpacity > 0) {
+				var h = Math.max(
+					document.documentElement.scrollHeight || document.body.scrollHeight,
+					dojo.html.getViewportHeight());
+				var w = dojo.html.getViewportWidth();
+				this.bg.style.width = w + "px";
+				this.bg.style.height = h + "px";
+			}
+			this.bgIframe.onResized();
+		},
+
+		showBackground: function() {
+			this.sizeBackground();
+			if(this.bgOpacity > 0) {
+				this.bg.style.display = "block";
+			}
+		},
+
+		placeDialog: function() {
+			var scroll_offset = dojo.html.getScrollOffset();
+			var viewport_size = dojo.html.getViewportSize();
+
+			// find the size of the dialog
+			var w = dojo.style.getOuterWidth(this.containerNode);
+			var h = dojo.style.getOuterHeight(this.containerNode);
+
+			var x = scroll_offset[0] + (viewport_size[0] - w)/2;
+			var y = scroll_offset[1] + (viewport_size[1] - h)/2;
+
+			with(this.domNode.style) {
+				left = x + "px";
+				top = y + "px";
+			}
+		},
+
+		show: function() {
+			this.setBackgroundOpacity();
+			this.showBackground();
+
+			dojo.widget.html.Dialog.superclass.show.call(this);
+
+			// FIXME: moz doesn't generate onscroll events for mouse or key scrolling (wtf)
+			// we should create a fake event by polling the scrolltop/scrollleft every X ms.
+			// this smells like it should be a dojo feature rather than just for this widget.
+
+			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.bg, "onclick", this, "hide");
+				}else{
+					dojo.event.disconnect(this.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.placeDialog();
+		},
+		
+		fillInTemplate: function(){
+			// dojo.event.connect(this.domNode, "onclick", this, "killEvent");
+		},
+
+		hide: function(){
+			// workaround for FF focus going into outer space
+			if (this.focusElement) { 
+				dojo.byId(this.focusElement).focus(); 
+				dojo.byId(this.focusElement).blur();
+			}
+			
+			if(this.timer){
+				clearInterval(this.timer);
+			}
+
+			this.bg.style.display = "none";
+			this.bg.style.width = this.bg.style.height = "1px";
+
+			dojo.widget.html.Dialog.superclass.hide.call(this);
+
+			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.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.placeDialog();
+			this.domNode.style.display = "block";
+		},
+
+		// Called when the browser window's size is changed
+		checkSize: function() {
+			if(this.isShowing()){
+				this.sizeBackground();
+				this.placeDialog();
+				this.domNode.style.display="block";
+				this.onResized();
+			}
+		},
+		
+		killEvent: function(evt){
+			evt.preventDefault();
+			evt.stopPropagation();
+		}
+
+	}
+);


Property changes on: trunk/PhotoOp/root/static/js/src/widget/Dialog.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/DocPane.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/DocPane.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/DocPane.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -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.provide("dojo.widget.DocPane");
+dojo.requireAfterIf("html", "dojo.widget.html.DocPane");


Property changes on: trunk/PhotoOp/root/static/js/src/widget/DocPane.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/DomWidget.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/DomWidget.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/DomWidget.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,593 @@
+/*
+	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.DomWidget");
+
+dojo.require("dojo.event.*");
+dojo.require("dojo.widget.Widget");
+dojo.require("dojo.dom");
+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, templateCssPath, templateString, avoidCache){
+	// dojo.debug("avoidCache:", avoidCache);
+	var tpath = templatePath || obj.templatePath;
+	var cpath = templateCssPath || obj.templateCssPath;
+
+	// 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");
+	}
+	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");
+	}
+	
+	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;
+
+	if(cpath && !dojo.widget._cssFiles[cpath.toString()]){
+		if((!obj.templateCssString)&&(cpath)){
+			obj.templateCssString = dojo.hostenv.getText(cpath);
+			obj.templateCssPath = null;
+		}
+		if((obj["templateCssString"])&&(!obj.templateCssString["loaded"])){
+			dojo.style.insertCssText(obj.templateCssString, null, cpath);
+			if(!obj.templateCssString){ obj.templateCssString = ""; }
+			obj.templateCssString.loaded = true;
+		}
+		dojo.widget._cssFiles[cpath.toString()] = true;
+	}
+
+	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:",
+				nsName: "role"
+	},
+	waiState: { name: "waiState", 
+				namespace: "http://www.w3.org/2005/07/aaa" , 
+				alias: "aaa",
+				prefix: "",
+				nsName: "state"
+	},
+	setAttr: function(node, attr, value){
+		if(dojo.render.html.ie){
+			node.setAttribute(this[attr].alias+":"+this[attr].nsName, this[attr].prefix+value);
+		}else{
+			node.setAttributeNS(this[attr].namespace, this[attr].nsName, this[attr].prefix+value);
+		}
+	}
+};
+
+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){
+				dojo.widget.wai.setAttr(baseNode, wai.name, val);
+			}
+		}, 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, {
+	initializer: 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{
+			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 = document.body; }
+			if(!cn){ cn = document.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);
+		
+		// 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["dojo:"+this.widgetType.toLowerCase()] ){
+			dojo.raise("Error: no frag for widget type " + this.widgetType +
+				", id " + this.widgetId + " (maybe a widget has set it's type incorrectly)");
+		}
+		return (frag ? frag["dojo:"+this.widgetType.toLowerCase()]["nodeRef"] : null);
+	},
+	
+	// Replace source domNode with generated dom structure, and register
+	// widget with parent.
+	postInitialize: function(args, frag, parentComp){
+		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;
+		}
+
+		// Expand my children widgets
+		if(this.isContainer){
+			//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];
+		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["templatecsspath"]){
+			args["templateCssPath"] = args["templatecsspath"];
+		}
+		if(args["templatepath"]){
+			avoidCache = true;
+			args["templatePath"] = args["templatepath"];
+		}
+		dojo.widget.fillFromTemplateCache(	this, 
+											args["templatePath"], 
+											args["templateCssPath"],
+											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("weren't able to 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");
+	}
+});


Property changes on: trunk/PhotoOp/root/static/js/src/widget/DomWidget.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/DropdownButton.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/DropdownButton.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/DropdownButton.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -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");


Property changes on: trunk/PhotoOp/root/static/js/src/widget/DropdownButton.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/DropdownContainer.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/DropdownContainer.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/DropdownContainer.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,118 @@
+/*
+	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.DropdownContainer");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.HtmlWidget");
+dojo.require("dojo.event.*");
+dojo.require("dojo.html");
+
+dojo.widget.defineWidget(
+	"dojo.widget.DropdownContainer",
+	dojo.widget.HtmlWidget,
+	{
+		initializer: function(){
+		},
+
+		inputWidth: "7em",
+		inputId: "",
+		inputName: "",
+		iconURL: dojo.uri.dojoUri("src/widget/templates/images/combo_box_arrow.png"),
+		iconAlt: "",
+
+		inputNode: null,
+		buttonNode: null,
+		containerNode: null,
+		subWidgetNode: null,
+
+		containerToggle: "plain",
+		containerToggleDuration: 150,
+		containerAnimInProgress: false,
+
+		templateString: '<div><span style="white-space:nowrap"><input type="text" value="" style="vertical-align:middle;" dojoAttachPoint="inputNode" autocomplete="off" /> <img src="${this.iconURL}" alt="${this.iconAlt}" dojoAttachPoint="buttonNode" dojoAttachEvent="onclick: onIconClick;" style="vertical-align:middle; cursor:pointer; cursor:hand;" /></span><br /><div dojoAttachPoint="containerNode" style="display:none;position:absolute;width:12em;background-color:#fff;"></div></div>',
+		templateCssPath: "",
+
+		fillInTemplate: function(args, frag){
+			var source = this.getFragNodeRef(frag);
+			
+			this.containerNode.style.left = "";
+			this.containerNode.style.top = "";
+
+			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");
+			
+			this.containerIframe = new dojo.html.BackgroundIframe(this.containerNode);
+			this.containerIframe.size([0,0,0,0]);
+		},
+
+		postMixInProperties: function(args, frag, parentComp){
+			// now that we know the setting for toggle, get toggle object
+			// (default to plain toggler if user specified toggler not present)
+			this.containerToggleObj =
+				dojo.lfx.toggle[this.containerToggle.toLowerCase()] || dojo.lfx.toggle.plain;
+			dojo.widget.DropdownContainer.superclass.postMixInProperties.call(this, args, frag, parentComp);
+		},
+
+		onIconClick: function(evt){
+			this.toggleContainerShow();
+		},
+
+		toggleContainerShow: function(){
+			if(dojo.html.isShowing(this.containerNode)){
+				this.hideContainer();
+			}else{
+				this.showContainer();
+			}
+		},
+		
+		showContainer: function(){
+			this.containerAnimInProgress=true;
+			this.containerToggleObj.show(this.containerNode, this.containerToggleDuration, null,
+				dojo.lang.hitch(this, this.onContainerShow), this.explodeSrc);
+			dojo.lang.setTimeout(this, this.sizeBackgroundIframe, this.containerToggleDuration);
+		},
+
+		onContainerShow: function(){
+			this.containerAnimInProgress=false;
+		},
+
+		hideContainer: function(){
+			this.containerAnimInProgress=true;
+			this.containerToggleObj.hide(this.containerNode, this.containerToggleDuration, null,
+				dojo.lang.hitch(this, this.onContainerHide), this.explodeSrc);
+			dojo.lang.setTimeout(this, this.sizeBackgroundIframe, this.containerToggleDuration);
+		},
+
+		onContainerHide: function(){
+			this.containerAnimInProgress=false;
+		},
+		
+		sizeBackgroundIframe: function(){
+			var w = dojo.style.getOuterWidth(this.containerNode);
+			var h = dojo.style.getOuterHeight(this.containerNode);
+			if(w==0||h==0){
+				// need more time to calculate size
+				dojo.lang.setTimeout(this, "sizeBackgroundIframe", 100);
+				return;
+			}
+			if(dojo.html.isShowing(this.containerNode)){
+				this.containerIframe.size([0,0,w,h]);
+			}
+		},
+
+		onInputChange: function(){}
+	},
+	"html"
+);
+
+dojo.widget.tags.addParseTreeHandler("dojo:dropdowncontainer");


Property changes on: trunk/PhotoOp/root/static/js/src/widget/DropdownContainer.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/DropdownDatePicker.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/DropdownDatePicker.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/DropdownDatePicker.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,67 @@
+/*
+	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.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();
+		}
+	},
+	"html"
+);
+
+dojo.widget.tags.addParseTreeHandler("dojo:dropdowndatepicker");


Property changes on: trunk/PhotoOp/root/static/js/src/widget/DropdownDatePicker.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/Editor.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/Editor.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/Editor.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,533 @@
+/*
+	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
+*/
+
+/* TODO:
+ * - font selector
+ * - test, bug fix, more features :)
+*/
+dojo.provide("dojo.widget.Editor");
+dojo.provide("dojo.widget.html.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.html.Editor = function() {
+	dojo.widget.HtmlWidget.call(this);
+	this.contentFilters = [];
+	this._toolbars = [];
+}
+dojo.inherits(dojo.widget.html.Editor, dojo.widget.HtmlWidget);
+
+dojo.widget.html.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.html.Editor.formatBlockValues = {
+	"Normal": "p",
+	"Main heading": "h2",
+	"Sub heading": "h3",
+	"Sub sub heading": "h4",
+	"Preformatted": "pre"
+};
+
+dojo.widget.html.Editor.fontNameValues = {
+	"Arial": "Arial, Helvetica, sans-serif",
+	"Verdana": "Verdana, sans-serif",
+	"Times New Roman": "Times New Roman, serif",
+	"Courier": "Courier New, monospace"
+};
+
+dojo.widget.html.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.html.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.html.Editor.supportedCommands = ["save", "cancel", "|", "-", "/", " "];
+
+dojo.lang.extend(dojo.widget.html.Editor, {
+	widgetType: "Editor",
+
+	saveUrl: "",
+	saveMethod: "post",
+	saveArgName: "editorContent",
+	closeOnSave: false,
+	items: dojo.widget.html.Editor.defaultItems,
+	formatBlockItems: dojo.lang.shallowCopy(dojo.widget.html.Editor.formatBlockValues),
+	fontNameItems: dojo.lang.shallowCopy(dojo.widget.html.Editor.fontNameValues),
+	fontSizeItems: dojo.lang.shallowCopy(dojo.widget.html.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.html.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.html.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.html.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.html.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(){}
+});
+


Property changes on: trunk/PhotoOp/root/static/js/src/widget/Editor.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/Editor2.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/Editor2.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/Editor2.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,395 @@
+/*
+	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
+*/
+
+/* TODO:
+ * - font selector
+ * - test, bug fix, more features :)
+*/
+dojo.provide("dojo.widget.Editor2");
+dojo.provide("dojo.widget.html.Editor2");
+dojo.require("dojo.io.*");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.RichText");
+dojo.require("dojo.widget.Editor2Toolbar");
+// dojo.require("dojo.widget.ColorPalette");
+// dojo.require("dojo.string.extras");
+
+dojo.widget.defineWidget(
+	"dojo.widget.html.Editor2",
+	dojo.widget.html.RichText,
+	{
+		saveUrl: "",
+		saveMethod: "post",
+		saveArgName: "editorContent",
+		closeOnSave: false,
+		shareToolbar: false,
+		toolbarAlwaysVisible: false,
+		htmlEditing: false,
+		_inHtmlMode: false,
+		_htmlEditNode: null,
+
+		commandList: dojo.widget.html.Editor2Toolbar.prototype.commandList,
+		toolbarWidget: null,
+		scrollInterval: null,
+		
+
+		editorOnLoad: function(){
+			var toolbars = dojo.widget.byType("Editor2Toolbar");
+			if((!toolbars.length)||(!this.shareToolbar)){
+				var tbOpts = {};
+				tbOpts.templatePath = dojo.uri.dojoUri("src/widget/templates/HtmlEditorToolbarOneline.html");
+				this.toolbarWidget = dojo.widget.createWidget("Editor2Toolbar", 
+										tbOpts, this.domNode, "before");
+				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.editNode, "onfocus");
+			// dojo.event.topic.registerPublisher("Editor2.clobberFocus", this.editNode, "onclick");
+			dojo.event.topic.subscribe("Editor2.clobberFocus", this, "setBlur");
+			dojo.event.connect(this.editNode, "onfocus", this, "setFocus");
+			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");
+					}
+				})
+			);
+
+			var focusFunc = dojo.lang.hitch(this, function(){ 
+				if(dojo.render.html.ie){
+					this.editNode.focus();
+				}else{
+					this.window.focus(); 
+				}
+			});
+
+			dojo.event.connect(this.toolbarWidget, "formatSelectClick", focusFunc);
+			dojo.event.connect(this, "execCommand", focusFunc);
+
+			if(this.htmlEditing){
+				var tb = this.toolbarWidget.htmltoggleButton;
+				if(tb){
+					tb.style.display = "";
+					dojo.event.connect(this.toolbarWidget, "htmltoggleClick",
+										this, "toggleHtmlEditing");
+				}
+			}
+		},
+
+		toggleHtmlEditing: function(){
+			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.style.getInnerHeight(this.editNode)+"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(){
+			// dojo.debug("setFocus:", 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 ds = dojo.style;
+			var tdn = this.toolbarWidget.domNode;
+			var db = document["body"];
+			var totalHeight = ds.getOuterHeight(tdn);
+			if(!this._scrollSetUp){
+				this._scrollSetUp = true;
+				var editorWidth =  ds.getOuterWidth(this.domNode); 
+				this._scrollThreshold = ds.abs(tdn, false).y;
+				// dojo.debug("threshold:", this._scrollThreshold);
+				if((isIE)&&(db)&&(ds.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.style.abs(tdn).x;
+						document.body.appendChild(tdn);
+						tdn.style.left = cl+dojo.style.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.style.getInnerHeight(this.editNode) : 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
+
+			dojo.lang.forEach(this.commandList, function(cmd){
+					if(cmd == "inserthtml"){ 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){
+				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");
+				}
+			}
+		},
+
+		wireUpOnLoad: function(){
+			if(!dojo.render.html.ie){
+				/*
+				dojo.event.kwConnect({
+					srcObj:		this.document,
+					srcFunc:	"click", 
+					targetObj:	this.toolbarWidget,
+					targetFunc:	"hideAllDropDowns",
+					once:		true
+				});
+				*/
+			}
+		}
+	},
+	"html",
+	function(){
+		var cp = dojo.widget.html.Editor2.prototype;
+		if(!cp._wrappersSet){
+			cp._wrappersSet = true;
+			cp.fillInTemplate = (function(fit){
+				return function(){
+					fit.call(this);
+					this.editorOnLoad();
+				};
+			})(cp.fillInTemplate);
+		
+			cp.onDisplayChanged = (function(odc){
+				return function(){
+					try{
+						odc.call(this);
+						this.updateToolbar();
+					}catch(e){}
+				};
+			})(cp.onDisplayChanged);
+
+			cp.onLoad = (function(ol){
+				return function(){
+					ol.call(this);
+					this.wireUpOnLoad();
+				};
+			})(cp.onLoad);
+		}
+	}
+);


Property changes on: trunk/PhotoOp/root/static/js/src/widget/Editor2.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/Editor2Toolbar.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/Editor2Toolbar.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/Editor2Toolbar.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,319 @@
+/*
+	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.Editor2Toolbar");
+dojo.provide("dojo.widget.html.Editor2Toolbar");
+
+dojo.require("dojo.lang.*");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.event.*");
+dojo.require("dojo.widget.RichText");
+dojo.require("dojo.widget.ColorPalette");
+
+dojo.widget.defineWidget(
+	"dojo.widget.html.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/HtmlEditorToolbar.html"),
+		templateCssPath: dojo.uri.dojoUri("src/widget/templates/HtmlEditorToolbar.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,
+
+		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(){ },
+
+		styleDropdownClick: function(){
+			dojo.debug("styleDropdownClick:", this.styleDropdownContainer);
+			dojo.style.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.style.hide(tb.forecolorDropDown);
+					dojo.style.hide(tb.hilitecolorDropDown);
+					dojo.style.hide(tb.styleDropdownContainer);
+					if(tb.clickInterceptDiv){
+						dojo.style.hide(tb.clickInterceptDiv);
+					}
+				}catch(e){}
+				if(dojo.render.html.ie){
+					try{
+						dojo.style.hide(tb.forecolorPalette.bgIframe);
+					}catch(e){}
+					try{
+						dojo.style.hide(tb.hilitecolorPalette.bgIframe);
+					}catch(e){}
+				}
+			});
+		},
+
+		selectFormat: function(format){
+			dojo.lang.forEach(this.formatSelectBox.options, function(item){
+				if(item.value.toLowerCase() == format.toLowerCase()){
+					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.style.toggleShowing(dd);
+			if(!pal){
+				pal = this[type+"Palette"] = dojo.widget.createWidget("ColorPalette", {}, dd, "first");
+				var fcp = pal.domNode;
+				with(dd.style){
+					width = dojo.html.getOuterWidth(fcp) + "px";
+					height = dojo.html.getOuterHeight(fcp) + "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.style, "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.style.toggleShowing(this.clickInterceptDiv);
+			var pos = dojo.style.abs(this[type+"Button"]);
+			dojo.html.placeOnScreenPoint(dd, pos.x, pos.y, 0, false);
+			if(pal.bgIframe){
+				with(pal.bgIframe.style){
+					display = "block";
+					left = dd.style.left;
+					top = dd.style.top;
+					width = dojo.style.getOuterWidth(dd)+"px";
+					height = dojo.style.getOuterHeight(dd)+"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.html.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;
+			}
+		}));
+	}
+);


Property changes on: trunk/PhotoOp/root/static/js/src/widget/Editor2Toolbar.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/FisheyeList.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/FisheyeList.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/FisheyeList.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,743 @@
+/*
+	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.FisheyeList");
+dojo.provide("dojo.widget.html.FisheyeList");
+dojo.provide("dojo.widget.html.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.dom");
+dojo.require("dojo.html");
+dojo.require("dojo.style");
+dojo.require("dojo.event");
+
+dojo.widget.tags.addParseTreeHandler("dojo:FisheyeList");
+dojo.widget.tags.addParseTreeHandler("dojo:FisheyeListItem");
+
+dojo.widget.html.FisheyeList = function(){
+	dojo.widget.HtmlWidget.call(this);
+}
+dojo.inherits(dojo.widget.html.FisheyeList, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.html.FisheyeList, {
+
+	templateString: '<div class="dojoHtmlFisheyeListBar"></div>',
+	templateCssPath: dojo.uri.dojoUri("src/widget/templates/HtmlFisheyeList.css"),
+	widgetType: "FisheyeList",
+
+	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(document.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 labelW = dojo.style.getOuterWidth(itm.lblNode);
+		var labelH = dojo.style.getOuterHeight(itm.lblNode);
+
+		if (this.labelEdge == this.EDGE.TOP){
+			x = Math.round((itm.sizeW / 2) - (labelW / 2));
+			y = -labelH;
+		}
+
+		if (this.labelEdge == this.EDGE.BOTTOM){
+			x = Math.round((itm.sizeW / 2) - (labelW / 2));
+			y = itm.sizeH;
+		}
+
+		if (this.labelEdge == this.EDGE.LEFT){
+			x = -labelW;
+			y = Math.round((itm.sizeH / 2) - (labelH / 2));
+		}
+
+		if (this.labelEdge == this.EDGE.RIGHT){
+			x = itm.sizeW;
+			y = Math.round((itm.sizeH / 2) - (labelH / 2));
+		}
+
+		itm.lblNode.style.left = x + 'px';
+		itm.lblNode.style.top  = y + 'px';
+	},
+
+	calcHitGrid: function(){
+
+		var pos = dojo.style.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.html.FisheyeList.superclass.destroy.call(this);
+	}
+});
+
+dojo.widget.html.FisheyeListItem = function(){
+	dojo.widget.HtmlWidget.call(this);
+}
+dojo.inherits(dojo.widget.html.FisheyeListItem, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.html.FisheyeListItem, {
+	widgetType: "FisheyeListItem",
+	
+	// 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)){
+			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() {
+	}
+});
+


Property changes on: trunk/PhotoOp/root/static/js/src/widget/FisheyeList.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/FloatingPane.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/FloatingPane.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/FloatingPane.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,305 @@
+/*
+	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.FloatingPane");
+dojo.provide("dojo.widget.html.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.shadow");
+dojo.require("dojo.style");
+dojo.require("dojo.dom");
+dojo.require("dojo.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.html.FloatingPane = function(){
+	dojo.widget.html.ContentPane.call(this);
+}
+
+dojo.inherits(dojo.widget.html.FloatingPane, dojo.widget.html.ContentPane);
+
+dojo.lang.extend(dojo.widget.html.FloatingPane, {
+	widgetType: "FloatingPane",
+
+	// 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/HtmlFloatingPane.html"),
+	templateCssPath: dojo.uri.dojoUri("src/widget/templates/HtmlFloatingPane.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)
+		document.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.dom.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="";
+			var rh = dojo.widget.createWidget("ResizeHandle", {targetElmId: this.widgetId, id:this.widgetId+"_resize"});
+			this.resizeBar.appendChild(rh.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();
+		}
+
+		if (dojo.hostenv.post_load_) {
+			this.setInitialWindowState();
+		} else {
+			dojo.addOnLoad(this, "setInitialWindowState");
+		}
+
+		// counteract body.appendChild above
+		document.body.removeChild(this.domNode);
+
+		dojo.widget.html.FloatingPane.superclass.fillInTemplate.call(this, args, frag);
+	},
+
+	postCreate: function(){
+		if(this.isShowing()){
+			this.width=-1;	// force resize
+			this.resizeTo(dojo.style.getOuterWidth(this.domNode), dojo.style.getOuterHeight(this.domNode));
+		}
+	},
+
+	maximizeWindow: function(evt) {
+		this.previous={
+			width: dojo.style.getOuterWidth(this.domNode) || this.width,
+			height: dojo.style.getOuterHeight(this.domNode) || this.height,
+			left: this.domNode.style.left,
+			top: this.domNode.style.top,
+			bottom: this.domNode.style.bottom,
+			right: this.domNode.style.right
+		};
+		this.domNode.style.left =
+			dojo.style.getPixelValue(this.domNode.parentNode, "padding-left", true) + "px";
+		this.domNode.style.top =
+			dojo.style.getPixelValue(this.domNode.parentNode, "padding-top", true) + "px";
+
+		if ((this.domNode.parentNode.nodeName.toLowerCase() == 'body')) {
+			this.resizeTo(
+				dojo.html.getViewportWidth()-dojo.style.getPaddingWidth(document.body),
+				dojo.html.getViewportHeight()-dojo.style.getPaddingHeight(document.body)
+			);
+		} else {
+			this.resizeTo(
+				dojo.style.getContentWidth(this.domNode.parentNode),
+				dojo.style.getContentHeight(this.domNode.parentNode)
+			);
+		}
+		this.maximizeAction.style.display="none";
+		this.restoreAction.style.display="";
+		this.windowState="maximized";
+	},
+
+	minimizeWindow: function(evt) {
+		this.hide();
+		this.windowState = "minimized";
+	},
+
+	restoreWindow: function(evt) {
+		if (this.windowState=="minimized") {
+			this.show() 
+		} else {
+			for(var attr in this.previous){
+				this.domNode.style[attr] = this.previous[attr];
+			}
+			this.resizeTo(this.previous.width, this.previous.height);
+			this.previous=null;
+
+			this.restoreAction.style.display="none";
+			this.maximizeAction.style.display=this.displayMaximizeAction ? "" : "none";
+		}
+
+		this.windowState="normal";
+	},
+
+	closeWindow: function(evt) {
+		dojo.dom.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;
+		}
+	},
+
+	setInitialWindowState: function() {
+		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.html.FloatingPane.superclass.show.apply(this, arguments);
+		this.bringToTop();
+	},
+
+	onShow: function(){
+		dojo.widget.html.FloatingPane.superclass.onShow.call(this);
+		this.resizeTo(dojo.style.getOuterWidth(this.domNode), dojo.style.getOuterHeight(this.domNode));
+	},
+
+	// This is called when the user adjusts the size of the floating pane
+	resizeTo: function(w, h){
+		dojo.style.setOuterWidth(this.domNode, w);
+		dojo.style.setOuterHeight(this.domNode, h);
+
+		dojo.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.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.widget.tags.addParseTreeHandler("dojo:FloatingPane");


Property changes on: trunk/PhotoOp/root/static/js/src/widget/FloatingPane.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/GoogleMap.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/GoogleMap.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/GoogleMap.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,44 @@
+/*
+	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.GoogleMap");
+dojo.provide("dojo.widget.GoogleMap.Controls");
+dojo.require("dojo.widget.*");
+dojo.widget.tags.addParseTreeHandler("dojo:googlemap");
+
+dojo.widget.GoogleMap=function(){
+	//	summary
+	//	base class for the Google Map widget
+	dojo.widget.Widget.call(this);
+	this.widgetType="GoogleMap";
+	this.isContainer=false;
+}
+dojo.inherits(dojo.widget.GoogleMap, dojo.widget.Widget);
+
+dojo.widget.GoogleMap.Controls={
+	LargeMap:"largemap",
+	SmallMap:"smallmap",
+	SmallZoom:"smallzoom",
+	Scale:"scale",
+	MapType:"maptype",
+	Overview:"overview",
+	get:function(s){
+		for(var p in this){
+			if(typeof(this[p])=="string"
+				&& this[p]==s
+			){
+				return p;
+			}
+		}
+		return null;
+	}
+};
+
+dojo.requireAfterIf("html", "dojo.widget.html.GoogleMap");


Property changes on: trunk/PhotoOp/root/static/js/src/widget/GoogleMap.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/HslColorPicker.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/HslColorPicker.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/HslColorPicker.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,25 @@
+/*
+	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.HslColorPicker");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.Widget");
+dojo.require("dojo.graphics.color");
+dojo.widget.tags.addParseTreeHandler("dojo:hslcolorpicker");
+
+dojo.requireAfterIf(dojo.render.svg.support.builtin, "dojo.widget.svg.HslColorPicker");
+
+dojo.widget.HslColorPicker=function(){
+	dojo.widget.Widget.call(this);
+	this.widgetType = "HslColorPicker";
+	this.isContainer = false;
+}
+dojo.inherits(dojo.widget.HslColorPicker, dojo.widget.Widget);

Added: trunk/PhotoOp/root/static/js/src/widget/HtmlWidget.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/HtmlWidget.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/HtmlWidget.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,163 @@
+/*
+	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.HtmlWidget");
+dojo.require("dojo.widget.DomWidget");
+dojo.require("dojo.html");
+dojo.require("dojo.html.extras");
+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){
+				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.style.isShowing(this.domNode);
+	},
+
+	toggleShowing: function(){
+		// dojo.style.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.
+		w=w||dojo.style.getOuterWidth(this.domNode);
+		h=h||dojo.style.getOuterHeight(this.domNode);
+		if(this.width == w && this.height == h){ return false; }
+
+		this.width=w;
+		this.height=h;
+		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.style.setOuterWidth(this.domNode, w);
+		dojo.style.setOuterHeight(this.domNode, 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){ child.checkSize(); });
+	}
+});


Property changes on: trunk/PhotoOp/root/static/js/src/widget/HtmlWidget.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/InlineEditBox.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/InlineEditBox.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/InlineEditBox.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,172 @@
+/*
+	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.InlineEditBox");
+dojo.provide("dojo.widget.html.InlineEditBox");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.lfx.*");
+dojo.require("dojo.graphics.color");
+dojo.require("dojo.string");
+dojo.require("dojo.style");
+dojo.require("dojo.html");
+
+dojo.widget.tags.addParseTreeHandler("dojo:inlineeditbox");
+
+dojo.widget.html.InlineEditBox = function(){
+	dojo.widget.HtmlWidget.call(this);
+	// mutable objects need to be in constructor to give each instance its own copy
+	this.history = [];
+}
+
+dojo.inherits(dojo.widget.html.InlineEditBox, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.html.InlineEditBox, {
+	templatePath: dojo.uri.dojoUri("src/widget/templates/HtmlInlineEditBox.html"),
+	templateCssPath: dojo.uri.dojoUri("src/widget/templates/HtmlInlineEditBox.css"),
+	widgetType: "InlineEditBox",
+
+	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.dom.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.style.getStyle(this.editable, "font-size");
+		ee.style.fontWeight = dojo.style.getStyle(this.editable, "font-weight");
+		ee.style.fontStyle = dojo.style.getStyle(this.editable, "font-style");
+		ee.style.width = Math.max(dojo.html.getInnerWidth(this.editable), this.minWidth) + "px";
+		if(this.mode.toLowerCase()=="textarea"){
+			ee.style.display = "block";
+			ee.style.height = Math.max(dojo.html.getInnerHeight(this.editable), 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.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 = this.textValue;
+		}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;
+		}
+	}
+});


Property changes on: trunk/PhotoOp/root/static/js/src/widget/InlineEditBox.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/LayoutContainer.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/LayoutContainer.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/LayoutContainer.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,70 @@
+/*
+	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 widget provides Delphi-style panel layout semantics
+//
+
+dojo.provide("dojo.widget.LayoutContainer");
+dojo.provide("dojo.widget.html.LayoutContainer");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.html.layout");
+
+dojo.widget.html.LayoutContainer = function(){
+	dojo.widget.HtmlWidget.call(this);
+}
+
+dojo.inherits(dojo.widget.html.LayoutContainer, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.html.LayoutContainer, {
+	widgetType: "LayoutContainer",
+	isContainer: true,
+
+	layoutChildPriority: 'top-bottom',
+
+	postCreate: function(){
+		dojo.html.layout(this.domNode, this.children, this.layoutChildPriority);
+	},
+
+	addChild: function(child, overrideContainerNode, pos, ref, insertIndex){
+		dojo.widget.html.LayoutContainer.superclass.addChild.call(this, child, overrideContainerNode, pos, ref, insertIndex);
+		dojo.html.layout(this.domNode, this.children, this.layoutChildPriority);
+	},
+
+	removeChild: function(pane){
+		dojo.widget.html.LayoutContainer.superclass.removeChild.call(this,pane);
+		dojo.html.layout(this.domNode, this.children, this.layoutChildPriority);
+	},
+
+	onResized: function(){
+		dojo.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.html.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'
+});
+
+dojo.widget.tags.addParseTreeHandler("dojo:LayoutContainer");


Property changes on: trunk/PhotoOp/root/static/js/src/widget/LayoutContainer.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/LinkPane.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/LinkPane.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/LinkPane.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,17 @@
+/*
+	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.LinkPane");
+
+dojo.require("dojo.widget.*");
+dojo.requireAfterIf("html", "dojo.widget.html.LinkPane");
+dojo.widget.tags.addParseTreeHandler("dojo:LinkPane");
+
+// NOTE: there's no stub file for this widget


Property changes on: trunk/PhotoOp/root/static/js/src/widget/LinkPane.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/Manager.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/Manager.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/Manager.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,306 @@
+/*
+	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.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){
+		var tw = this.widgets[widgetIndex].widgetId;
+		delete this.widgetIds[tw];
+		this.widgets.splice(widgetIndex, 1);
+	}
+	
+	// FIXME: suboptimal performance
+	this.removeById = function(id) {
+		for (var i=0; i<this.widgets.length; i++){
+			if(this.widgets[i].widgetId == id){
+				this.remove(i);
+				break;
+			}
+		}
+	}
+
+	this.getWidgetById = function(id){
+		return this.widgetIds[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.getWidgetsOfType = function (id) {
+		dojo.deprecated("getWidgetsOfType", "use getWidgetsByType", "0.4");
+		return dojo.widget.manager.getWidgetsByType(id);
+	}
+
+	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();
+		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){
+		// try and find a name for the widget
+		var impl = this.getImplementationName(widgetName);
+		if(impl){ 
+			// var tic = new Date();
+			var ret = new impl(ctorObject);
+			// dojo.debug(new Date() - tic);
+			return ret;
+		}
+	}
+
+	this.getImplementationName = function(widgetName){
+		/*
+		 * 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.
+		 */
+
+		var lowerCaseWidgetName = widgetName.toLowerCase();
+
+		var impl = knownWidgetImplementations[lowerCaseWidgetName];
+		if(impl){
+			return impl;
+		}
+
+		// 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("");
+		}
+
+		// 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; }
+
+			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[lowerCaseWidgetName] =
+						widgetPackage[renderPrefixCache[j]][widgetClass];
+					return knownWidgetImplementations[lowerCaseWidgetName];
+				}
+			}
+
+			for (var j = 0; j < renderPrefixCache.length; j++) {
+				for (var widgetClass in widgetPackage) {
+					if (widgetClass.toLowerCase() !=
+						(renderPrefixCache[j] + lowerCaseWidgetName)) { continue; }
+	
+					knownWidgetImplementations[lowerCaseWidgetName] =
+						widgetPackage[widgetClass];
+					return knownWidgetImplementations[lowerCaseWidgetName];
+				}
+			}
+		}
+		
+		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;
+})();


Property changes on: trunk/PhotoOp/root/static/js/src/widget/Manager.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/Menu.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/Menu.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/Menu.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -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");


Property changes on: trunk/PhotoOp/root/static/js/src/widget/Menu.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/Menu2.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/Menu2.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/Menu2.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,1190 @@
+/*
+	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.Menu2");
+dojo.provide("dojo.widget.html.Menu2");
+dojo.provide("dojo.widget.PopupMenu2");
+dojo.provide("dojo.widget.MenuItem2");
+dojo.provide("dojo.widget.MenuBar2");
+
+dojo.require("dojo.html");
+dojo.require("dojo.style");
+dojo.require("dojo.event.*");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.HtmlWidget");
+
+
+dojo.widget.PopupMenu2 = function(){
+	dojo.widget.HtmlWidget.call(this);
+	this.items = [];	// unused???
+	this.targetNodeIds = []; // fill this with nodeIds upon widget creation and it becomes context menu for those nodes
+	this.queueOnAnimationFinish = [];
+
+	this.eventNames =  {
+		open: ""
+	};
+
+}
+
+dojo.inherits(dojo.widget.PopupMenu2, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.PopupMenu2, {
+	widgetType: "PopupMenu2",
+	isContainer: true,
+
+	snarfChildDomOutput: true,
+
+	currentSubmenu: null,
+	currentSubmenuTrigger: null,
+	parentMenu: null,
+	parentMenuBar: null,
+	isShowingNow: false,
+	menuX: 0,
+	menuY: 0,
+	menuWidth: 0,
+	menuHeight: 0,
+	menuIndex: 0,
+
+	domNode: null,
+	containerNode: null,
+
+	eventNaming: "default",
+
+
+	templateString: '<div class="dojoPopupMenu2" style="left:-9999px; top:-9999px; display: none;"><div dojoAttachPoint="containerNode" class="dojoPopupMenu2Client"></div></div>',
+	templateCssPath: dojo.uri.dojoUri("src/widget/templates/HtmlMenu2.css"),
+
+	itemHeight: 18,
+	iconGap: 1,
+	accelGap: 10,
+	submenuGap: 2,
+	finalGap: 5,
+	submenuIconSize: 4,
+	separatorHeight: 9,
+	submenuDelay: 500,
+	submenuOverlap: 5,
+	contextMenuForWindow: false,
+	openEvent: null,
+
+	submenuIconSrc: dojo.uri.dojoUri("src/widget/templates/images/submenu_off.gif").toString(),
+	submenuIconOnSrc: dojo.uri.dojoUri("src/widget/templates/images/submenu_on.gif").toString(),
+
+	initialize: function(args, frag) {
+
+		if (this.eventNaming == "default") {
+			for (var eventName in this.eventNames) {
+				this.eventNames[eventName] = this.widgetId+"/"+eventName;
+			}
+		}
+
+	},
+
+	postCreate: function(){
+		if (this.domNode.style.display=="none"){
+			this.domNode.style.display = "";
+		}
+		this.domNode.style.left = '-9999px'
+		this.domNode.style.top = '-9999px'
+
+		// attach menu to document body if it's not already there
+		if (this.domNode.parentNode != document.body){
+			document.body.appendChild(this.domNode);
+		}
+
+
+		if (this.contextMenuForWindow){
+			var doc = document.documentElement  || document.body;
+			dojo.widget.Menu2.OperaAndKonqFixer.fixNode(doc);
+			dojo.event.connect(doc, "oncontextmenu", this, "onOpen");
+		} else if ( this.targetNodeIds.length > 0 ){
+			for(var i=0; i<this.targetNodeIds.length; i++){
+				this.bindDomNode(this.targetNodeIds[i]);
+			}
+		}
+
+		this.subscribeSubitemsOnOpen();
+
+		this.layoutMenuSoon();
+	},
+
+	subscribeSubitemsOnOpen: function() {
+		var subItems = this.getChildrenOfType(dojo.widget.MenuItem2);
+
+		//dojo.debug(subItems)
+
+		for(var i=0; i<subItems.length; i++) {
+			//dojo.debug(subItems[i]);
+			dojo.event.topic.subscribe(this.eventNames.open, subItems[i], "menuOpen")
+		}
+	},
+
+	// get open event for current menu
+	getTopOpenEvent: function() {
+		var menu = this;
+		while (menu.parent){ menu = menu.parent; }
+		return menu.openEvent;
+	},
+
+	// attach menu to given node
+	bindDomNode: function(nodeName){
+		var node = dojo.byId(nodeName);
+
+		// 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
+		});
+	},
+
+	// 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);
+	},
+
+	layoutMenuSoon: function(){
+		dojo.lang.setTimeout(this, "layoutMenu", 0);
+	},
+
+	layoutMenu: function(){
+
+        // menu must be attached to DOM for size calculations to work
+		// even though we attached to document.body in postCreate(), here
+		// we seem to be attached to a #document-fragment.  Don't understand why.
+        document.body.appendChild(this.domNode);
+
+        // determine menu width
+		var max_label_w = 0;
+		var max_accel_w = 0;
+
+		for(var i=0; i<this.children.length; i++){
+			if (this.children[i].getLabelWidth){
+				max_label_w = Math.max(max_label_w, this.children[i].getLabelWidth());
+			}
+
+			if (dojo.lang.isFunction(this.children[i].getAccelWidth)){
+				max_accel_w = Math.max(max_accel_w, this.children[i].getAccelWidth());
+			}
+		}
+
+		if( isNaN(max_label_w) || isNaN(max_accel_w) ){
+			// Browser needs some more time to calculate sizes
+			this.layoutMenuSoon();
+			return;
+		}
+
+		var clientLeft = dojo.style.getPixelValue(this.domNode, "padding-left", true) + dojo.style.getPixelValue(this.containerNode, "padding-left", true);
+		var clientTop  = dojo.style.getPixelValue(this.domNode, "padding-top", true)  + dojo.style.getPixelValue(this.containerNode, "padding-top", true);
+
+		if( isNaN(clientLeft) || isNaN(clientTop) ){
+			// Browser needs some more time to calculate sizes
+			this.layoutMenuSoon();
+			return;
+		}
+
+		var y = clientTop;
+		var max_item_width = 0;
+
+		for(var i=0; i<this.children.length; i++){
+
+			var ch = this.children[i];
+
+			ch.layoutItem(max_label_w, max_accel_w);
+
+			ch.topPosition = y;
+
+			y += dojo.style.getOuterHeight(ch.domNode);
+			max_item_width = Math.max(max_item_width, dojo.style.getOuterWidth(ch.domNode));
+		}
+
+		dojo.style.setContentWidth(this.containerNode, max_item_width);
+		dojo.style.setContentHeight(this.containerNode, y-clientTop);
+
+		dojo.style.setContentWidth(this.domNode, dojo.style.getOuterWidth(this.containerNode));
+		dojo.style.setContentHeight(this.domNode, dojo.style.getOuterHeight(this.containerNode));
+
+		this.menuWidth = dojo.style.getOuterWidth(this.domNode);
+		this.menuHeight = dojo.style.getOuterHeight(this.domNode);
+	},
+
+	/**
+	 * Open the menu at position (x,y), relative to the viewport
+	 * (usually positions are relative to the document; why is this different??)
+	 */
+	open: function(x, y, parent, explodeSrc){
+
+		// if explodeSrc isn't specified then explode from my parent widget
+		explodeSrc = explodeSrc || parent["domNode"] || [];
+
+		if (this.isShowingNow){ return; }
+
+		var parentMenu = (parent && parent.widgetType=="PopupMenu2") ? parent : null;
+
+		if ( !parentMenu ) {
+			// record whenever a top level menu is opened
+			// explodeSrc may or may not be a node - it may also be an [x,y] position array
+			var button = explodeSrc instanceof Array ? null : explodeSrc;
+			dojo.widget.html.Menu2Manager.opened(this, button);
+		}
+
+		//dojo.debug("open called for animation "+this.animationInProgress)
+
+		// 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 viewport = dojo.html.getViewportSize();
+		var scrolloffset = dojo.html.getScrollOffset();
+
+		var clientRect = {
+			'left'  : scrolloffset[0],
+			'right' : scrolloffset[0] + viewport[0],
+			'top'   : scrolloffset[1],
+			'bottom': scrolloffset[1] + viewport[1]
+		};
+
+		if (parentMenu){
+			// submenu is opening
+
+			if (x + this.menuWidth > clientRect.right){ x = x - (this.menuWidth + parentMenu.menuWidth - (2 * this.submenuOverlap)); }
+
+			if (y + this.menuHeight > clientRect.bottom){ y = y -
+			(this.menuHeight - (this.itemHeight + 5)); } // TODO: why 5?
+
+		}else{
+			// top level menu is opening
+			x+=scrolloffset[0];
+			y+=scrolloffset[1];
+			explodeSrc[0] += scrolloffset[0];
+			explodeSrc[1] += scrolloffset[1];
+
+			if (x < clientRect.left){ x = clientRect.left; }
+			if (x + this.menuWidth > clientRect.right){ x = x - this.menuWidth; }
+
+			if (y < clientRect.top){ y = clientRect.top; }
+			if (y + this.menuHeight > clientRect.bottom){ y = y - this.menuHeight; }
+		}
+
+		this.parentMenu = parentMenu;
+		this.explodeSrc = explodeSrc;
+		this.menuIndex = parentMenu ? parentMenu.menuIndex + 1 : 1;
+
+		this.menuX = x;
+		this.menuY = y;
+
+		// move the menu into position but make it invisible
+		// (because when menus are initially constructed they are visible but off-screen)
+		this.domNode.style.zIndex = 200 + this.menuIndex;
+		this.domNode.style.left = x + 'px';
+		this.domNode.style.top = y + 'px';
+		this.domNode.style.display='none';
+		this.domNode.style.position='absolute';
+
+		// then use the user defined method to display it
+		this.show();
+
+		this.isShowingNow = true;
+	},
+
+	close: function(){
+		// If we are in the process of opening the menu and we are asked to close it,
+		// we should really cancel the current animation, but for simplicity we will
+		// just ignore the request
+		if(this.animationInProgress){
+			this.queueOnAnimationFinish.push(this.close, []);
+			return;
+		}
+
+		this.closeSubmenu();
+		this.hide();
+		this.isShowingNow = false;
+		dojo.widget.html.Menu2Manager.closed(this);
+
+		if (this.parentMenuBar){
+			this.parentMenuBar.closedMenu(this);
+		}
+	},
+
+	onShow: function() {
+		dojo.widget.HtmlWidget.prototype.onShow.call(this);
+		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);
+
+		this.processQueue();
+	},
+
+
+	closeAll: function(){
+		if (this.parentMenu){
+			this.parentMenu.closeAll();
+		}else{
+			this.close();
+		}
+	},
+
+	closeSubmenu: function(){
+		if (this.currentSubmenu == null){ return; }
+
+		this.currentSubmenu.close();
+		this.currentSubmenu = null;
+
+		this.currentSubmenuTrigger.is_open = false;
+		this.currentSubmenuTrigger.closedSubmenu();
+		this.currentSubmenuTrigger = null;
+	},
+
+	openSubmenu: function(submenu, from_item){
+
+		var our_x = dojo.style.getPixelValue(this.domNode, 'left');
+		var our_y = dojo.style.getPixelValue(this.domNode, 'top');
+		var our_w = dojo.style.getOuterWidth(this.domNode);
+		var item_y = from_item.topPosition;
+
+		var x = our_x + our_w - this.submenuOverlap;
+		var y = our_y + item_y;
+
+		this.currentSubmenu = submenu;
+		this.currentSubmenu.open(x, y, this, from_item.domNode);
+
+		this.currentSubmenuTrigger = from_item;
+		this.currentSubmenuTrigger.is_open = true;
+	},
+
+	onOpen: function(e){
+		this.openEvent = e;
+
+		//dojo.debugShallow(e);
+		this.open(e.clientX, e.clientY, null, [e.clientX, e.clientY]);
+
+		if(e["preventDefault"]){
+			e.preventDefault();
+		}
+	},
+
+	isPointInMenu: function(x, y){
+
+		if (x < this.menuX){ return false; }
+		if (x > this.menuX + this.menuWidth){ return false; }
+
+		if (y < this.menuY){ return false; }
+		if (y > this.menuY + this.menuHeight){ return false; }
+
+		return true;
+	}
+});
+
+
+dojo.widget.MenuItem2 = function(){
+	dojo.widget.HtmlWidget.call(this);
+
+	this.eventNames = {
+		engage: ""
+	};
+}
+
+dojo.inherits(dojo.widget.MenuItem2, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.MenuItem2, {
+	widgetType: "MenuItem2",
+	templateString:
+			 '<div class="dojoMenuItem2">'
+			+'<div dojoAttachPoint="iconNode" class="dojoMenuItem2Icon"></div>'
+			+'<span dojoAttachPoint="labelNode" class="dojoMenuItem2Label"><span><span></span></span></span>'
+			+'<span dojoAttachPoint="accelNode" class="dojoMenuItem2Accel"><span><span></span></span></span>'
+			+'<div dojoAttachPoint="submenuNode" class="dojoMenuItem2Submenu"></div>'
+			+'<div dojoAttachPoint="targetNode" class="dojoMenuItem2Target" dojoAttachEvent="onMouseOver: onHover; onMouseOut: onUnhover; onClick: _onClick;">&nbsp;</div>'
+			+'</div>',
+
+	//
+	// nodes
+	//
+
+	domNode: null,
+	iconNode: null,
+	labelNode: null,
+	accelNode: null,
+	submenuNode: null,
+	targetNode: null,
+
+	//
+	// internal settings
+	//
+
+	is_hovering: false,
+	hover_timer: null,
+	is_open: false,
+	topPosition: 0,
+
+	//
+	// options
+	//
+
+	caption: 'Untitled',
+	accelKey: '',
+	iconSrc: '',
+	submenuId: '',
+	disabled: false,
+	eventNaming: "default",
+
+
+	postCreate: function(){
+
+		dojo.html.disableSelection(this.domNode);
+
+		if (this.disabled){
+			this.setDisabled(true);
+		}
+
+		this.labelNode.childNodes[0].appendChild(document.createTextNode(this.caption));
+		this.accelNode.childNodes[0].appendChild(document.createTextNode(this.accelKey));
+
+		this.labelShadowNode = this.labelNode.childNodes[0].childNodes[0];
+		this.accelShadowNode = this.accelNode.childNodes[0].childNodes[0];
+
+		this.labelShadowNode.appendChild(document.createTextNode(this.caption));
+		this.accelShadowNode.appendChild(document.createTextNode(this.accelKey));
+
+		if (this.eventNaming == "default") {
+			for (var eventName in this.eventNames) {
+				this.eventNames[eventName] = this.widgetId+"/"+eventName;
+			}
+		}
+	},
+
+	layoutItem: function(label_w, accel_w){
+
+		var x_label = this.parent.itemHeight + this.parent.iconGap;
+		var x_accel = x_label + label_w + this.parent.accelGap;
+		var x_submu = x_accel + accel_w + this.parent.submenuGap;
+		var total_w = x_submu + this.parent.submenuIconSize + this.parent.finalGap;
+
+
+		this.iconNode.style.left = '0px';
+		this.iconNode.style.top = '0px';
+
+
+		if (this.iconSrc){
+
+			if ((this.iconSrc.toLowerCase().substring(this.iconSrc.length-4) == ".png") && (dojo.render.html.ie)){
+
+				this.iconNode.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+this.iconSrc+"', sizingMethod='image')";
+				this.iconNode.style.backgroundImage = '';
+			}else{
+				this.iconNode.style.backgroundImage = 'url('+this.iconSrc+')';
+			}
+		}else{
+			this.iconNode.style.backgroundImage = '';
+		}
+
+		dojo.style.setOuterWidth(this.iconNode, this.parent.itemHeight);
+		dojo.style.setOuterHeight(this.iconNode, this.parent.itemHeight);
+
+		dojo.style.setOuterHeight(this.labelNode, this.parent.itemHeight);
+		dojo.style.setOuterHeight(this.accelNode, this.parent.itemHeight);
+
+		dojo.style.setContentWidth(this.domNode, total_w);
+		dojo.style.setContentHeight(this.domNode, this.parent.itemHeight);
+
+		this.labelNode.style.left = x_label + 'px';
+		this.accelNode.style.left = x_accel + 'px';
+		this.submenuNode.style.left = x_submu + 'px';
+
+		dojo.style.setOuterWidth(this.submenuNode, this.parent.submenuIconSize);
+		dojo.style.setOuterHeight(this.submenuNode, this.parent.itemHeight);
+
+		this.submenuNode.style.display = this.submenuId ? 'block' : 'none';
+		this.submenuNode.style.backgroundImage = 'url('+this.parent.submenuIconSrc+')';
+
+		dojo.style.setOuterWidth(this.targetNode, total_w);
+		dojo.style.setOuterHeight(this.targetNode, this.parent.itemHeight);
+	},
+
+	onHover: function(){
+
+		if (this.is_hovering){ return; }
+		if (this.is_open){ return; }
+
+		this.parent.closeSubmenu();
+		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.stopSubmenuTimer();
+	},
+
+	// Internal function for clicks
+	_onClick: function(){
+		if (this.disabled){ return; }
+
+		if (this.submenuId){
+			if (!this.is_open){
+				this.stopSubmenuTimer();
+				this.openSubmenu();
+			}
+		}else{
+			this.parent.closeAll();
+		}
+
+		// for some browsers the onMouseOut doesn't get called (?), so call it manually
+		this.onUnhover();
+
+		// user defined handler for click
+		this.onClick();
+
+		dojo.event.topic.publish(this.eventNames.engage, this);
+	},
+
+	// User defined function to handle clicks
+	onClick: function() { },
+
+	highlightItem: function(){
+		dojo.html.addClass(this.domNode, 'dojoMenuItem2Hover');
+		this.submenuNode.style.backgroundImage = 'url('+this.parent.submenuIconOnSrc+')';
+	},
+
+	unhighlightItem: function(){
+		dojo.html.removeClass(this.domNode, 'dojoMenuItem2Hover');
+		this.submenuNode.style.backgroundImage = 'url('+this.parent.submenuIconSrc+')';
+	},
+
+	startSubmenuTimer: function(){
+		this.stopSubmenuTimer();
+
+		if (this.disabled){ return; }
+
+		var self = this;
+		var closure = function(){ return function(){ self.openSubmenu(); } }();
+
+		this.hover_timer = window.setTimeout(closure, this.parent.submenuDelay);
+	},
+
+	stopSubmenuTimer: function(){
+		if (this.hover_timer){
+			window.clearTimeout(this.hover_timer);
+			this.hover_timer = null;
+		}
+	},
+
+	openSubmenu: function(){
+		// first close any other open submenu
+		this.parent.closeSubmenu();
+
+		var submenu = dojo.widget.getWidgetById(this.submenuId);
+		if (submenu){
+
+			this.parent.openSubmenu(submenu, this);
+		}
+
+		//dojo.debug('open submenu for item '+this.widgetId);
+	},
+
+	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');
+		}
+	},
+
+	getLabelWidth: function(){
+
+		var node = this.labelNode.childNodes[0];
+
+		return dojo.style.getOuterWidth(node);
+	},
+
+	getAccelWidth: function(){
+
+		var node = this.accelNode.childNodes[0];
+
+		return dojo.style.getOuterWidth(node);
+	},
+
+	menuOpen: function(message) {
+	}
+
+});
+
+
+dojo.widget.MenuSeparator2 = function(){
+	dojo.widget.HtmlWidget.call(this);
+}
+
+dojo.inherits(dojo.widget.MenuSeparator2, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.MenuSeparator2, {
+	widgetType: "MenuSeparator2",
+
+	domNode: null,
+	topNode: null,
+	bottomNode: null,
+
+	templateString: '<div class="dojoMenuSeparator2">'
+			+'<div dojoAttachPoint="topNode" class="dojoMenuSeparator2Top"></div>'
+			+'<div dojoAttachPoint="bottomNode" class="dojoMenuSeparator2Bottom"></div>'
+			+'</div>',
+
+	postCreate: function(){
+		dojo.html.disableSelection(this.domNode);
+		this.layoutItem();
+	},
+
+	layoutItem: function(label_w, accel_w){
+
+		var full_width = this.parent.itemHeight
+				+ this.parent.iconGap
+				+ label_w
+				+ this.parent.accelGap
+				+ accel_w
+				+ this.parent.submenuGap
+				+ this.parent.submenuIconSize
+				+ this.parent.finalGap;
+
+		if (isNaN(full_width)){ return; }
+
+		dojo.style.setContentHeight(this.domNode, this.parent.separatorHeight);
+		dojo.style.setContentWidth(this.domNode, full_width);
+	}
+});
+
+//
+// the menu manager makes sure we don't have several menus
+// open at once. the root menu in an opening sequence calls
+// opened(). when a root menu closes it calls closed(). then
+// everything works. lovely.
+//
+
+dojo.widget.html.Menu2Manager = new function(){
+
+	this.currentMenu = null;
+	this.currentButton = null;		// button that opened current menu (if any)
+	this.focusNode = null;
+
+	dojo.event.connect(document, 'onmousedown', this, 'onClick');
+	dojo.event.connect(window, "onscroll", this, "onClick");
+
+	this.closed = function(menu){
+		if (this.currentMenu == menu){
+			this.currentMenu = null;
+			this.currentButton = null;
+		}
+	};
+
+	this.opened = function(menu, button){
+		if (menu == this.currentMenu){ return; }
+
+		if (this.currentMenu){
+			this.currentMenu.close();
+		}
+
+		this.currentMenu = menu;
+		this.currentButton = button;
+	};
+
+	this.onClick = function(e){
+
+		if (!this.currentMenu){ return; }
+
+		var scrolloffset = dojo.html.getScrollOffset();
+
+		var x = e.clientX + scrolloffset[0];
+		var y = e.clientY + scrolloffset[1];
+
+		var m = this.currentMenu;
+
+		// starting from the base menu, perform a hit test
+		// and exit when one succeeds
+
+		while (m){
+
+			if (m.isPointInMenu(x, y)){
+
+				return;
+			}
+
+			m = m.currentSubmenu;
+		}
+
+		// 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 does its supported and we dont need to implement our own
+ 	*/
+
+ 	// gets called if we have support for oncontextmenu
+ 	if (!dojo.lang.isFunction(document.oncontextmenu)){
+ 		document.oncontextmenu = function(){
+ 			implement = false;
+ 			delfunc = true;
+ 		}
+ 	}
+
+ 	if (document.createEvent){ // moz, safari has contextmenu event, need to do livecheck on this env.
+ 		try {
+ 			var e = document.createEvent("MouseEvents");
+ 			e.initMouseEvent("contextmenu", 1, 1, window, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, null);
+ 			document.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 document.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 (window.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.MenuBar2 = function(){
+	dojo.widget.HtmlWidget.call(this);
+}
+
+dojo.inherits(dojo.widget.MenuBar2, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.MenuBar2, {
+	widgetType: "MenuBar2",
+	isContainer: true,
+
+	snarfChildDomOutput: true,
+
+	currentItem: null,
+	isExpanded: false,
+
+	currentSubmenu: null,
+	currentSubmenuTrigger: null,
+
+	domNode: null,
+	containerNode: null,
+
+	templateString: '<div class="dojoMenuBar2"><div dojoAttachPoint="containerNode" class="dojoMenuBar2Client"></div></div>',
+	templateCssPath: dojo.uri.dojoUri("src/widget/templates/HtmlMenu2.css"),
+
+	itemHeight: 18,
+	openEvent: null,
+
+
+	postCreate: function(){
+
+		// do something here
+
+		this.layoutMenuSoon();
+	},
+
+	layoutMenuSoon: function(){
+		dojo.lang.setTimeout(this, "layoutMenu", 0);
+	},
+
+	layoutMenu: function(){
+
+		// menu must be attached to DOM for size calculations to work
+
+		var parent = this.domNode.parentNode;
+		if (! parent || parent == undefined) {
+			document.body.appendChild(this.domNode);
+		}
+
+
+		// determine menu height
+
+		var max_label_h = 0;
+
+		for(var i=0; i<this.children.length; i++){
+
+			if (this.children[i].getLabelHeight){
+
+				max_label_h = Math.max(max_label_h, this.children[i].getLabelHeight());
+			}
+		}
+
+		if (isNaN(max_label_h)){
+			// Browser needs some more time to calculate sizes
+			this.layoutMenuSoon();
+			return;
+		}
+
+		var clientLeft = dojo.style.getPixelValue(this.domNode, "padding-left", true)
+				+ dojo.style.getPixelValue(this.containerNode, "margin-left", true)
+				+ dojo.style.getPixelValue(this.containerNode, "padding-left", true);
+		var clientTop  = dojo.style.getPixelValue(this.domNode, "padding-top", true)
+				+ dojo.style.getPixelValue(this.containerNode, "padding-top", true);
+
+		if (isNaN(clientLeft) || isNaN(clientTop)){
+			// Browser needs some more time to calculate sizes
+			this.layoutMenuSoon();
+			return;
+		}
+
+		var max_item_height = 0;
+		var x = clientLeft;
+
+		for (var i=0; i<this.children.length; i++){
+
+			var ch = this.children[i];
+
+			ch.layoutItem(max_label_h);
+
+			ch.leftPosition = x;
+			ch.domNode.style.left = x + 'px';
+
+			x += dojo.style.getOuterWidth(ch.domNode);
+			max_item_height = Math.max(max_item_height, dojo.style.getOuterHeight(ch.domNode));
+		}
+
+		dojo.style.setContentHeight(this.containerNode, max_item_height);
+		dojo.style.setContentHeight(this.domNode, dojo.style.getOuterHeight(this.containerNode));
+	},
+
+	openSubmenu: function(submenu, from_item){
+
+		var our_pos = dojo.style.getAbsolutePosition(this.domNode, false);
+
+		var our_h = dojo.style.getOuterHeight(this.domNode);
+		var item_x = from_item.leftPosition;
+
+		var x = our_pos.x + item_x;
+		var y = our_pos.y + our_h;
+
+		this.currentSubmenu = submenu;
+		this.currentSubmenu.open(x, y, this, from_item.domNode);
+		this.currentSubmenu.parentMenuBar = this;
+	},
+
+	closeSubmenu: function(){
+
+		if (this.currentSubmenu == null){ return; }
+
+		var menu = this.currentSubmenu;
+		this.currentSubmenu = null;
+		menu.close();
+	},
+
+	itemHover: function(item){
+
+		if (item == this.currentItem) return;
+
+		if (this.currentItem){
+			this.currentItem.unhighlightItem();
+
+			if (this.isExpanded){
+				this.closeSubmenu();
+			}
+		}
+
+		this.currentItem = item;
+		this.currentItem.highlightItem();
+
+		if (this.isExpanded){
+			this.currentItem.expandMenu();
+		}
+	},
+
+	itemUnhover: function(item){
+
+		if (item != this.currentItem) return;
+
+		if (this.currentItem && !this.isExpanded){
+			this.currentItem.unhighlightItem();
+			this.currentItem = null;
+		}
+	},
+
+	itemClick: function(item){
+
+		if (item != this.currentItem){
+
+			this.itemHover(item);
+		}
+
+		if (this.isExpanded){
+
+			this.isExpanded = false;
+			this.closeSubmenu();
+
+		}else{
+
+			this.isExpanded = true;
+			this.currentItem.expandMenu();
+		}
+	},
+
+	closedMenu: function(menu){
+
+		if (this.currentSubmenu == menu){
+
+			this.isExpanded = false;
+			this.itemUnhover(this.currentItem);
+		}
+	}
+});
+
+
+dojo.widget.MenuBarItem2 = function(){
+	dojo.widget.HtmlWidget.call(this);
+}
+
+dojo.inherits(dojo.widget.MenuBarItem2, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.MenuBarItem2, {
+
+	widgetType: "MenuBarItem2",
+	templateString:
+			 '<div class="dojoMenuBarItem2">'
+			+'<span dojoAttachPoint="labelNode" class="dojoMenuBarItem2Label"><span><span></span></span></span>'
+			+'<div dojoAttachPoint="targetNode" class="dojoMenuBarItem2Target" dojoAttachEvent="onMouseOver: onHover; onMouseOut: onUnhover; onClick: _onClick;">&nbsp;</div>'
+			+'</div>',
+
+	//
+	// nodes
+	//
+
+	domNode: null,
+	labelNode: null,
+	targetNode: null,
+
+	//
+	// internal settings
+	//
+
+	is_hovering: false,
+	hover_timer: null,
+	is_open: false,
+
+	//
+	// options
+	//
+
+	caption: 'Untitled',
+	accelKey: '',
+	iconSrc: '',
+	submenuId: '',
+	disabled: false,
+	eventNaming: "default",
+
+
+	postCreate: function(){
+
+		dojo.html.disableSelection(this.domNode);
+
+		if (this.disabled){
+			this.setDisabled(true);
+		}
+
+		this.labelNode.childNodes[0].appendChild(document.createTextNode(this.caption));
+
+		this.labelShadowNode = this.labelNode.childNodes[0].childNodes[0];
+		this.labelShadowNode.appendChild(document.createTextNode(this.caption));
+
+		if (this.eventNaming == "default") {
+			for (var eventName in this.eventNames) {
+				this.eventNames[eventName] = this.widgetId+"/"+eventName;
+			}
+		}
+	},
+
+	layoutItem: function(item_h){
+
+		var label_w = dojo.style.getOuterWidth(this.labelNode);
+
+		var clientLeft = dojo.style.getPixelValue(this.domNode, "padding-left", true);
+		var clientTop  = dojo.style.getPixelValue(this.domNode, "padding-top", true);
+
+		this.labelNode.style.left = clientLeft + 'px';
+
+		dojo.style.setOuterHeight(this.labelNode, item_h);
+		dojo.style.setContentWidth(this.domNode, label_w);
+		dojo.style.setContentHeight(this.domNode, item_h);
+
+		this.labelNode.style.left = '0px';
+
+		dojo.style.setOuterWidth(this.targetNode, label_w);
+		dojo.style.setOuterHeight(this.targetNode, item_h);
+	},
+
+	getLabelHeight: function(){
+
+		return dojo.style.getOuterHeight(this.labelNode);
+	},
+
+	onHover: function(){
+		this.parent.itemHover(this);
+	},
+
+	onUnhover: function(){
+		this.parent.itemUnhover(this);
+	},
+
+	_onClick: function(){
+		this.parent.itemClick(this);
+	},
+
+	highlightItem: function(){
+		dojo.html.addClass(this.domNode, 'dojoMenuBarItem2Hover');
+	},
+
+	unhighlightItem: function(){
+		dojo.html.removeClass(this.domNode, 'dojoMenuBarItem2Hover');
+	},
+
+	expandMenu: function(){
+
+		var submenu = dojo.widget.getWidgetById(this.submenuId);
+		if (submenu){
+
+			this.parent.openSubmenu(submenu, this);
+		}
+	},
+
+	setDisabled: function(value){
+		this.disabled = value;
+
+		if (this.disabled){
+			dojo.html.addClass(this.domNode, 'dojoMenuBarItem2Disabled');
+		}else{
+			dojo.html.removeClass(this.domNode, 'dojoMenuBarItem2Disabled');
+		}
+	}
+});
+
+// make it a tag
+dojo.widget.tags.addParseTreeHandler("dojo:MenuBar2");
+dojo.widget.tags.addParseTreeHandler("dojo:MenuBarItem2");
+dojo.widget.tags.addParseTreeHandler("dojo:PopupMenu2");
+dojo.widget.tags.addParseTreeHandler("dojo:MenuItem2");
+dojo.widget.tags.addParseTreeHandler("dojo:MenuSeparator2");
+


Property changes on: trunk/PhotoOp/root/static/js/src/widget/Menu2.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/MenuItem.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/MenuItem.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/MenuItem.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -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");


Property changes on: trunk/PhotoOp/root/static/js/src/widget/MenuItem.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/MonthlyCalendar.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/MonthlyCalendar.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/MonthlyCalendar.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,74 @@
+/*
+	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.MonthlyCalendar");
+dojo.provide("dojo.widget.MonthlyCalendar.util");
+dojo.require("dojo.widget.DomWidget");
+dojo.require("dojo.date");
+
+dojo.widget.MonthlyCalendar= function(){
+	dojo.widget.Widget.call(this);
+	this.widgetType = "MonthlyCalendar";
+	this.isContainer = false;
+	// the following aliases prevent breaking people using 0.2.x
+	this.months = dojo.date.months;
+	this.weekdays = dojo.date.days;
+	this.toRfcDate = dojo.widget.MonthlyCalendar.util.toRfcDate;
+	this.fromRfcDate = dojo.widget.MonthlyCalendar.util.fromRfcDate;
+	this.initFirstSaturday = dojo.widget.MonthlyCalendar.util.initFirstSaturday;
+}
+
+dojo.inherits(dojo.widget.MonthlyCalendar, dojo.widget.Widget);
+dojo.widget.tags.addParseTreeHandler("dojo:monthlycalendar");
+
+dojo.requireAfterIf("html", "dojo.widget.html.MonthlyCalendar");
+
+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));
+	}
+	
+	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()};
+	}
+}


Property changes on: trunk/PhotoOp/root/static/js/src/widget/MonthlyCalendar.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/Parse.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/Parse.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/Parse.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,312 @@
+/*
+	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.Parse");
+
+dojo.require("dojo.widget.Manager");
+dojo.require("dojo.dom");
+
+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();
+					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))&&(ltn.substr(0, 5)=="dojo:")){
+							dojo.debug("no tag handler registed 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;
+			// FIXME: propertySetId is not in scope here
+			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
+	*/
+	this.createComponentFromScript = function(nodeRef, componentName, properties){
+		var ltn = "dojo:" + componentName.toLowerCase();
+		if(dojo.widget.tags[ltn]){
+			properties.fastMixIn = true;
+			return [dojo.widget.tags[ltn](properties, this, null, null, properties)];
+		}else{
+			if(ltn.substr(0, 5)=="dojo:"){
+				dojo.debug("no tag handler registed 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
+ * @param props    Key-Value pairs of properties of the widget
+ * @param refNode  If the last argument is specified this node is used as
+ *                 a reference for inserting this node into a DOM tree else
+ *                 it beomces 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 lowerCaseName = name.toLowerCase();
+	var namespacedName = "dojo:" + lowerCaseName;
+	var isNode = ( dojo.byId(name) && (!dojo.widget.tags[namespacedName]) );
+
+	// if we got a node or an unambiguious ID, build a widget out of it
+	if(	(arguments.length==1) && ((typeof name != "string")||(isNode)) ){
+		// 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) {
+		props[namespacedName] = { 
+			dojotype: [{value: lowerCaseName}],
+			nodeRef: placeKeeperNode,
+			fastMixIn: true
+		};
+		return dojo.widget.getParser().createComponentFromScript(
+			placeKeeperNode, name, props, true);
+	}
+
+	if (typeof name != "string" && typeof props == "string") {
+		dojo.deprecated("dojo.widget.createWidget", 
+			"argument order is now of the form " +
+			"dojo.widget.createWidget(NAME, [PROPERTIES, [REFERENCENODE, [POSITION]]])", "0.4");
+		return fromScript(name, props, refNode);
+	}
+	
+	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){
+			document.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, props);
+	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
+}
+ 
+dojo.widget.fromScript = function(name, props, refNode, position){
+	dojo.deprecated("dojo.widget.fromScript", " use " +
+		"dojo.widget.createWidget instead", "0.4");
+	return dojo.widget.createWidget(name, props, refNode, position);
+}


Property changes on: trunk/PhotoOp/root/static/js/src/widget/Parse.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/PopUpButton.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/PopUpButton.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/PopUpButton.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -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;
+	}
+
+});


Property changes on: trunk/PhotoOp/root/static/js/src/widget/PopUpButton.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/ResizableTextarea.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/ResizableTextarea.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/ResizableTextarea.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,110 @@
+/*
+	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.ResizableTextarea");
+dojo.require("dojo.html");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.LayoutContainer");
+dojo.require("dojo.widget.ResizeHandle");
+
+dojo.widget.tags.addParseTreeHandler("dojo:resizabletextarea");
+
+dojo.widget.ResizableTextarea = function(){
+	dojo.widget.HtmlWidget.call(this);
+}
+
+dojo.inherits(dojo.widget.ResizableTextarea, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.ResizableTextarea, {
+	templatePath: dojo.uri.dojoUri("src/widget/templates/HtmlResizableTextarea.html"),
+	templateCssPath: dojo.uri.dojoUri("src/widget/templates/HtmlResizableTextarea.css"),
+	widgetType: "ResizableTextarea",
+	tagName: "dojo:resizabletextarea",
+	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!
+		document.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 = "";
+	}
+});


Property changes on: trunk/PhotoOp/root/static/js/src/widget/ResizableTextarea.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/ResizeHandle.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/ResizeHandle.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/ResizeHandle.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,112 @@
+/*
+	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.ResizeHandle");
+dojo.provide("dojo.widget.html.ResizeHandle");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.html");
+dojo.require("dojo.style");
+dojo.require("dojo.dom");
+dojo.require("dojo.event");
+
+dojo.widget.html.ResizeHandle = function(){
+	dojo.widget.HtmlWidget.call(this);
+}
+
+dojo.inherits(dojo.widget.html.ResizeHandle, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.html.ResizeHandle, {
+	widgetType: "ResizeHandle",
+
+	isSizing: false,
+	startPoint: null,
+	startSize: null,
+	minSize: null,
+
+	targetElmId: '',
+
+	templateCssPath: dojo.uri.dojoUri("src/widget/templates/HtmlResizeHandle.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};
+		this.startSize  = {'w':dojo.style.getOuterWidth(this.targetDomNode), 'h':dojo.style.getOuterHeight(this.targetDomNode)};
+
+		dojo.event.kwConnect({
+			srcObj: document.body, 
+			srcFunc: "onmousemove",
+			targetObj: this,
+			targetFunc: "changeSizing",
+			rate: 25
+		});
+		dojo.event.connect(document.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) {
+			if (newW < this.minSize.w) {
+				newW = dojo.style.getOuterWidth(this.targetDomNode);
+			}
+			if (newH < this.minSize.h) {
+				newH = dojo.style.getOuterHeight(this.targetDomNode);
+			}
+		}
+		
+		if(this.targetWidget){
+			this.targetWidget.resizeTo(newW, newH);
+		}else{
+			dojo.style.setOuterWidth(this.targetDomNode, newW);
+			dojo.style.setOuterHeight(this.targetDomNode, newH);
+		}
+		
+		e.preventDefault();
+	},
+
+	endSizing: function(e){
+		dojo.event.disconnect(document.body, "onmousemove", this, "changeSizing");
+		dojo.event.disconnect(document.body, "onmouseup", this, "endSizing");
+
+		this.isSizing = false;
+	}
+
+
+});
+
+dojo.widget.tags.addParseTreeHandler("dojo:ResizeHandle");


Property changes on: trunk/PhotoOp/root/static/js/src/widget/ResizeHandle.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/RichText.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/RichText.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/RichText.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,1451 @@
+/*
+	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
+*/
+
+ /* -*- tab-width: 4 -*- */
+dojo.provide("dojo.widget.RichText");
+dojo.provide("dojo.widget.html.RichText");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.dom");
+dojo.require("dojo.html");
+dojo.require("dojo.event.*");
+dojo.require("dojo.style");
+dojo.require("dojo.string");
+
+// used to save content
+try {
+	document.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.html.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__%%**@@",
+
+		// contentFilters: [],
+
+		/*
+		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) {
+			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 = "&nbsp;"; }
+				this.domNode = document.createElement("div");
+				with(this.textarea.style){
+					display = "block";
+					position = "absolute";
+					width = "1px";
+					height = "1px";
+					border = margin = padding = "0px";
+					visiblity = "hidden";
+					if(dojo.render.html.ie){
+						overflow = "hidden";
+					}
+				}
+				dojo.dom.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.dom.insertAfter(editor.textarea, editor.domNode);
+				});
+			}else{
+				var html = dojo.string.trim(this.domNode.innerHTML);
+				if(html == ""){ html = "&nbsp;"; }
+			}
+					
+			this._oldHeight = dojo.style.getContentHeight(this.domNode);
+			this._oldWidth = dojo.style.getContentWidth(this.domNode);
+
+			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");
+			}
+
+			// 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 && dojo.render.html.ie) { // active-x
+				this._drawObject(html);
+				// dojo.debug(this.object.document);
+			} else if (dojo.render.html.ie) { // contentEditable, easy
+				this.editNode = document.createElement("div");
+				with (this.editNode) {
+					innerHTML = html;
+					contentEditable = true;
+					style.height = this.height ? this.height : this.minHeight;
+				}
+
+				if(this.height){ this.editNode.style.overflowY="scroll"; }
+				// 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);
+
+				dojo.lang.forEach(this.events, function(e){
+					dojo.event.connect(this.editNode, e.toLowerCase(), this, e);
+				}, this);
+			
+				this.window = window;
+				this.document = document;
+				
+				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.style.getPixelValue(element, 
+										 'border-'+side+'-width', 
+										 false)) {
+				return false;
+			} else if (dojo.style.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.style.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.style.getStyle(element, "display") != "none" 
+					&& !dojo.style.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.style.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.style.getPixelValue(sibling, 
+															 siblingMarginProp, 
+															 false);
+					break;
+				}
+				sibling = sibling[siblingAttr];
+			}
+			if (!sibling) { // no sibling, look at parent's margin instead
+				contextMargin = dojo.style.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.dom.firstElement(tmpContent);
+			var lastChild = dojo.dom.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.style.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.style.getUnitValue(this.domNode, 'line-height');
+					if (lineHeightStyle.value && lineHeightStyle.units=="") {
+						lineHeight = lineHeightStyle.value;
+					}
+
+					dojo.style.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 ? '' : ' 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 = document.createElement("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"));
+			this.object.attachEvent("DisplayChanged", dojo.lang.hitch(this, "_updateHeight"));
+			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">' +
+				'<title></title>' +
+				'<style type="text/css">' +
+				'    body,html { padding: 0; margin: 0; }' + //font: ' + font + '; }' +
+				(this.height ? '' : '    body { overflow: hidden; }') +
+				//'    #bodywrapper {  }' +
+				'</style>' +
+				//'<base href="' + window.location + '">' +
+				'<body><div id="bodywrapper">' + html + '</div></body>';
+		},
+
+	/* 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 "&nbsp;" 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 <p> with an embedded <br> when you hit enter once:
+			//   <p><br>\n</p>
+			// when you hit enter again it adds another <br> inside your enter
+			//   <p><br>\n<br>\n</p>
+			// and if you hit enter again it splits the <br>s over 2 <p>s
+			//   <p><br>\n</p>\n<p><br>\n</p>
+			// now this assumes that <p>s have double the line-height of <br>s to work
+			// and so we need to remove the <p>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(/^<br>\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){ 
+			if( (dojo.render.html.mozilla)&&(this._initialFocus) ){
+				this._initialFocus = false;
+				if(dojo.string.trim(this.editNode.innerHTML) == "&nbsp;"){
+					this.execCommand("selectall");
+					this.window.getSelection().collapseToStart();
+				}
+			}
+		},
+
+		blur: function () {
+			if (this.iframe) { this.window.blur(); }
+			else if (this.editNode) { this.editNode.blur(); }
+		},
+		
+		focus: function () {
+			if(this.iframe){
+				this.window.focus();
+			}else if(this.editNode){
+				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 */
+		_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(drh.ie){ joinObject.args[1] = "<"+joinObject.args[1]+">"; }
+			}
+			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 "undo": case "redo":
+					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":
+					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":
+				case "cut": case "copy": case "paste": 
+					supportedBy = isSupportedBy(ie);
+					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;
+			if(this.object){
+				if(command == "forecolor"){
+					command = "setforecolor";
+				}else if(command == "backcolor"){
+					command = "setbackcolor";
+				}
+			
+				//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";
+						document.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);
+					return true;
+				}else if(arguments.length == 1){
+					return this.object.ExecCommand(this._activeX.command[command],
+						this._activeX.ui.noprompt);
+				}else{
+					return 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 range = document.createRange();
+				var a = this.getSelectedNode();
+				while(a.nodeName != "A"){ a = a.parentNode; }
+				range.selectNode(a);
+				selection.removeAllRanges();
+				selection.addRange(range);
+				
+				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 = "<tr>";
+				for (var i = 0; i < argument.cols; i++) { cols += "<td></td>"; }
+				cols += "</tr>";
+			
+				var table = "<table><tbody>";
+				for (var i = 0; i < argument.rows; i++) { table += cols; }
+				table += "</tbody></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") )){
+				// 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
+				}
+				returnValue = this.document.execCommand(command, false, argument);
+
+				// try{
+				// }catch(e){
+				// 	dojo.debug(e);
+				// }
+			}
+			
+			this.onDisplayChanged();
+			return returnValue;
+		},
+
+		queryCommandEnabled: 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 false; }
+				var status = this.object.QueryStatus(this._activeX.command[command]);
+				return ((status != this.activeX.status.notsupported)&& 
+					(status != this.activeX.status.diabled));
+			}else{
+				// mozilla returns true always
+				if(command == "unlink" && dojo.render.html.mozilla){
+					var node = this.getSelectedNode();
+					while (node.parentNode && node.nodeName != "A") { node = node.parentNode; }
+					return node.nodeName == "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.enabled)||
+					(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":
+					case "blockformat":
+						command = "get" + command;
+						return this.object.execCommand(
+							this._activeX.command[command],
+							this._activeX.ui.noprompt);
+				}			
+			
+				//var status = this.object.QueryStatus(this._activeX.command[command]);
+			} else {
+				return this.document.queryCommandValue(command);
+			}
+		},
+		
+		
+	/* Misc.
+	 ********/
+
+		getSelectedNode: function(){
+			if(!this.isLoaded){ return; }
+			if(this.document.selection){
+				return this.document.selection.createRange().parentElement();
+			}else if(dojo.render.html.mozilla){
+				return this.window.getSelection().getRangeAt(0).commonAncestorContainer;
+			}
+			return this.editNode;
+		},
+		
+		placeCursorAtStart: function(){
+			if(!this.isLoaded){
+				dojo.event.connect(this, "onLoad", this, "placeCursorAtEnd");
+				return;
+			}
+			dojo.event.disconnect(this, "onLoad", this, "placeCursorAtEnd");
+			if(this.window.getSelection){
+				var selection = this.window.getSelection;
+				if(selection.removeAllRanges){ // Mozilla
+					var range = this.document.createRange();
+					range.selectNode(this.editNode.firstChild);
+					range.collapse(true);
+					var selection = this.window.getSelection();
+					selection.removeAllRanges();
+					selection.addRange(range);
+				}else{ // Safari
+					// not a great deal we can do
+				}
+			}else if(this.document.selection){ // IE
+				var range = this.document.body.createTextRange();
+				range.moveToElementText(this.editNode);
+				range.collapse(true);
+				range.select();
+			}
+		},
+
+		replaceEditorContent: function(html){
+			if(this.window.getSelection){
+				var selection = this.window.getSelection;
+				// if(selection.removeAllRanges){ // Mozilla			
+				if(dojo.render.html.moz){ // Mozilla			
+					var range = this.document.createRange();
+					range.selectNodeContents(this.editNode);
+					var selection = this.window.getSelection();
+					selection.removeAllRanges();
+					selection.addRange(range);
+					this.execCommand("inserthtml", html);
+				}else{ // Safari
+					// look ma! it's a totally f'd browser!
+					this.editNode.innerHTML = html;
+				}
+			}else if(this.document.selection){ // IE
+				var range = this.document.body.createTextRange();
+				range.moveToElementText(this.editNode);
+				range.select();
+				this.execCommand("inserthtml", html);
+			}
+		},
+		
+		placeCursorAtEnd: function(){
+			if(!this.isLoaded){
+				dojo.event.connect(this, "onLoad", this, "placeCursorAtEnd");
+				return;
+			}
+			dojo.event.disconnect(this, "onLoad", this, "placeCursorAtEnd");
+			if(this.window.getSelection){
+				var selection = this.window.getSelection;
+				if(selection.removeAllRanges){ // Mozilla
+					var range = this.document.createRange();
+					range.selectNode(this.editNode.lastChild);
+					range.collapse(false);
+					var selection = this.window.getSelection();
+					selection.removeAllRanges();
+					selection.addRange(range);
+				}else{ // Safari
+					// not a great deal we can do
+				}
+			}else if(this.document.selection){ // IE
+				var range = this.document.body.createTextRange();
+				range.moveToElementText(this.editNode);
+				range.collapse(true);
+				range.select();
+			}
+		},
+
+		_lastHeight: 0,
+
+		/** Updates the height of the iframe to fit the contents. */
+		_updateHeight: function(){
+			if(!this.isLoaded){ return; }
+			if(this.height){ return; }
+			if(this.iframe){
+				/*
+				if(!this.document.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.style.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){
+				this.object.style.height = dojo.style.getInnerHeight(this.editNode)+"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) == "&nbsp;"){ ec = ""; }
+			}catch(e){ /* squelch */ }
+
+			dojo.lang.forEach(this.contentFilters, 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 contentFilters 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("(<img[^>]*\ 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]);
+			}
+		},
+
+		_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);
+				}
+			}	
+		}
+		
+	},
+	"html",
+	function(){
+		this.contentFilters = [];
+		// this.contentFilters.push(this.defaultContentCleaner);
+		
+		this._keyHandlers = {};
+	}
+);


Property changes on: trunk/PhotoOp/root/static/js/src/widget/RichText.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/Rounded.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/Rounded.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/Rounded.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,713 @@
+/*
+	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.Rounded");
+dojo.widget.tags.addParseTreeHandler("dojo:rounded");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.html.ContentPane");
+dojo.require("dojo.html");
+dojo.require("dojo.style");
+
+/*
+ *	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.Rounded = function() {
+	dojo.widget.html.ContentPane.call(this);
+}
+
+dojo.inherits(dojo.widget.Rounded, dojo.widget.html.ContentPane);
+
+dojo.lang.extend(dojo.widget.Rounded, {
+	isSafari: dojo.render.html.safari,
+	widgetType: "Rounded",
+	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.style.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.style.getStyle(this.box, "height");
+		if(boxHeight=="") boxHeight="0px";
+		var boxWidth        = dojo.style.getStyle(this.box, "width");
+		var borderWidth     = dojo.style.getStyle(this.box, "borderTopWidth");
+		if(borderWidth=="") borderWidth="0px";
+		//alert(borderWidth);
+
+		var borderColour    = dojo.style.getStyle(this.box, "borderTopColor");
+		// Set to true if we have a border
+		if(borderWidth>0) this.antiAlias=true;
+
+		var boxColour       = dojo.style.getStyle(this.box, "backgroundColor");
+		var backgroundImage = dojo.style.getStyle(this.box, "backgroundImage");
+		var boxPosition     = dojo.style.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.style.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


Property changes on: trunk/PhotoOp/root/static/js/src/widget/Rounded.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/Select.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/Select.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/Select.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,76 @@
+/*
+	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.Select");
+dojo.provide("dojo.widget.html.Select");
+
+dojo.require("dojo.widget.html.ComboBox");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.html.stabile");
+
+/*
+ * The Select widget is an enhanced version of HTML's <select> tag.
+ *
+ * Similar features:
+ *   - There is a drop down list of possible values.
+ *   - You can only enter a value from the drop down list.  (You can't enter an arbitrary value.)
+ *   - The value submitted with the form is the hidden value (ex: CA),
+       not the displayed value a.k.a. label (ex: California)
+ *
+ * Enhancements over plain HTML version:
+ *   - If you type in some text then it will filter down the list of possible values in the drop down list.
+ *   - List can be specified either as a static list or via a javascript function (that can get the list from a server)
+ */
+
+dojo.widget.defineWidget(
+	"dojo.widget.html.Select",
+	dojo.widget.html.ComboBox,
+	{
+		widgetType: "Select",
+		forceValidOption: true,
+
+		setValue: function(value) {
+			this.comboBoxValue.value = value;
+			dojo.widget.html.stabile.setState(this.widgetId, this.getState(), true);
+		},
+
+		setLabel: function(value){
+			// FIXME, not sure what to do here!
+			this.comboBoxSelectionValue.value = value;
+			if (this.textInputNode.value != value) { // prevent mucking up of selection
+				this.textInputNode.value = value;
+			}
+		},	  
+
+		getLabel: function(){
+			return this.comboBoxSelectionValue.value;
+		},
+
+		getState: function() {
+			return {
+				value: this.getValue(),
+				label: this.getLabel()
+			};
+		},
+
+		onKeyUp: function(evt){
+			this.setLabel(this.textInputNode.value);
+		},
+
+		setState: function(state) {
+			this.setValue(state.value);
+			this.setLabel(state.label);
+		},
+
+		setAllValues: function(value1, value2){
+			this.setValue(value2);
+			this.setLabel(value1);
+		}
+	});


Property changes on: trunk/PhotoOp/root/static/js/src/widget/Select.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/Show.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/Show.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/Show.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,45 @@
+/*
+	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.Show");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.lang.common");
+
+dojo.widget.Show = function(){}
+dojo.lang.extend(dojo.widget.Show, {
+	isContainer: true,
+	_slide: -1,
+	_slides: [],
+	gotoSlide: function(/*int*/ slide){
+		this._slide = slide;
+		// summary: Placeholder
+	},
+	nextSlide: function(/*Event?*/ event){
+		if(!this._slides[this._slide].nextAction(event)){
+			if((this._slide + 1) != this._slides.length){
+				this.gotoSlide(this._slide + 1);
+				return true; // boolean
+			}
+			return false; // boolean
+		}
+	},
+	previousSlide: function(/*Event?*/ event){
+		if(!this._slides[this._slide].previousAction(event)){
+			if((this._slide - 1) != -1){
+				this.gotoSlide(this._slide - 1);
+				return true; // boolean
+			}
+			return false; // boolean
+		}
+	}
+});
+
+dojo.requireAfterIf("html", "dojo.widget.html.Show");
\ No newline at end of file


Property changes on: trunk/PhotoOp/root/static/js/src/widget/Show.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/ShowAction.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/ShowAction.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/ShowAction.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,26 @@
+/*
+	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.ShowAction");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.lang.common");
+
+dojo.widget.ShowAction = function(){}
+dojo.lang.extend(dojo.widget.ShowAction, {
+	on: "",
+	action: "",
+	duration: 0,
+	from: "",
+	to: "",
+	auto: "false"
+});
+
+dojo.requireAfterIf("html", "dojo.widget.html.ShowAction");
\ No newline at end of file


Property changes on: trunk/PhotoOp/root/static/js/src/widget/ShowAction.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/ShowSlide.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/ShowSlide.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/ShowSlide.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,43 @@
+/*
+	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.ShowSlide");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.lang.common");
+
+dojo.widget.ShowSlide = function(){
+}
+dojo.lang.extend(dojo.widget.ShowSlide, {
+	title: "",
+	_action: -1,
+	isContainer: true,
+	_components: {},
+	_actions: [],
+	gotoAction: function(/*int*/ action){
+		this._action = action;
+	},
+	nextAction: function(/*Event?*/ event){
+		if((this._action + 1) != this._actions.length){
+			++this._action;
+			return true; // boolean
+		}
+		return false; // boolean
+	},
+	previousAction: function(/*Event?*/ event){
+		if((this._action - 1) != -1){
+			--this._action;
+			return true; // boolean
+		}
+		return false; // boolean
+	}
+});
+
+dojo.requireAfterIf("html", "dojo.widget.html.ShowSlide");
\ No newline at end of file


Property changes on: trunk/PhotoOp/root/static/js/src/widget/ShowSlide.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/SimpleDropdownButtons.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/SimpleDropdownButtons.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/SimpleDropdownButtons.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,158 @@
+/*
+	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
+*/
+
+/* TODO:
+ * - make the dropdowns "smart" so they can't get cutoff on bottom of page, sides of page, etc.
+ * - unify menus with the MenuItem and Menu classes so we can add stuff to all menus at once
+ * - allow buttons to be enabled/disabled at runtime
+ *     - this probably means creating all menus upfront and then triggering a disable action
+ *       for disabled buttons in the constructor loop. we'll need a disable and enable action anyway
+ * - should each button with menu be a widget object of it's own?
+ */
+dojo.provide("dojo.widget.SimpleDropdownButtons");
+dojo.provide("dojo.widget.HtmlSimpleDropdownButtons");
+
+dojo.deprecated("dojo.widget.SimpleDropdownButtons",  "use dojo.widget.DropDownButton", "0.4");
+
+dojo.require("dojo.event.*");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.uri.Uri");
+dojo.require("dojo.dom");
+dojo.require("dojo.style");
+dojo.require("dojo.html");
+
+dojo.widget.tags.addParseTreeHandler("dojo:simpledropdownbuttons");
+
+dojo.widget.HtmlSimpleDropdownButtons = function() {
+	dojo.widget.HtmlWidget.call(this);
+
+	this.widgetType = "SimpleDropdownButtons";
+	this.templateCssPath = dojo.uri.dojoUri("src/widget/templates/HtmlSimpleDropdownButtons.css");
+
+	this.menuTriggerClass = "dojoSimpleDropdownButtons";
+	this.menuClass = "dojoSimpleDropdownButtonsMenu";
+
+	// overwrite buildRendering so we don't clobber our list
+	this.buildRendering = function(args, frag) {
+		if(this.templateCssPath) {
+			dojo.style.insertCssFile(this.templateCssPath, null, true);
+		}
+		this.domNode = frag["dojo:"+this.widgetType.toLowerCase()]["nodeRef"];
+
+		var menu = this.domNode;
+		if( !dojo.html.hasClass(menu, this.menuTriggerClass) ) {
+			dojo.html.addClass(menu, this.menuTriggerClass);
+		}
+		var li = dojo.dom.getFirstChildElement(menu);
+		var menuIDs = [];
+		var arrowIDs = [];
+
+		while(li) {
+			if(li.getElementsByTagName("ul").length > 0) {
+				var a = dojo.dom.getFirstChildElement(li);
+				var arrow = document.createElement("a");
+				arrow.href = "javascript:;";
+				arrow.innerHTML = "&nbsp;";
+				dojo.html.setClass(arrow, "downArrow");
+				if(!arrow.id) {
+					arrow.id = dojo.dom.getUniqueId();
+				}
+				arrowIDs.push(arrow.id);
+				var submenu = dojo.dom.getNextSiblingElement(a);
+				if(!submenu.id) {
+					submenu.id = dojo.dom.getUniqueId();
+				}
+				menuIDs.push(submenu.id);
+
+				if( dojo.html.hasClass(a, "disabled") ) {
+					dojo.html.addClass(arrow, "disabled");
+					dojo.html.disableSelection(li);
+					arrow.onfocus = function(){ this.blur(); }
+				} else {
+					dojo.html.addClass(submenu, this.menuClass);
+					document.body.appendChild(submenu);
+					dojo.event.connect(arrow, "onmousedown", (function() {
+						var ar = arrow;
+						return function(e) {
+							dojo.html.addClass(ar, "pressed");
+						}
+					})());
+					dojo.event.connect(arrow, "onclick", (function() {
+						var aa = a;
+						var ar = arrow;
+						var sm = submenu;
+						var setWidth = false;
+
+						return function(e) {
+							hideAll(sm, ar);
+							sm.style.left = (dojo.html.getScrollLeft()
+								+ e.clientX - e.layerX + aa.offsetLeft) + "px";
+							sm.style.top = (dojo.html.getScrollTop() + e.clientY
+								- e.layerY + aa.offsetTop + aa.offsetHeight) + "px";
+							sm.style.display = sm.style.display == "block" ? "none" : "block";
+							if(sm.style.display == "none") {
+								dojo.html.removeClass(ar, "pressed");
+								e.target.blur()
+							}
+							if(!setWidth && sm.style.display == "block"
+								&& sm.offsetWidth < aa.offsetWidth + ar.offsetWidth) {
+								sm.style.width = aa.offsetWidth + ar.offsetWidth + "px";
+								setWidth = true;
+							}
+							e.preventDefault();
+						}
+					})());
+				}
+
+				dojo.event.connect(a, "onclick", function(e) {
+					if(e && e.target && e.target.blur) {
+						e.target.blur();
+					}
+				});
+
+				if(a.nextSibling) {
+					li.insertBefore(arrow, a.nextSibling);
+				} else {
+					li.appendChild(arrow);
+				}
+
+			}
+			li = dojo.dom.getNextSiblingElement(li);
+		}
+
+		function hideAll(excludeMenu, excludeArrow) {
+			// hide menus
+			for(var i = 0; i < menuIDs.length; i++) {
+				var m = document.getElementById(menuIDs[i]);
+				if(!excludeMenu || m != excludeMenu) {
+					document.getElementById(menuIDs[i]).style.display = "none";
+				}
+			}
+			// restore arrows to non-pressed state
+			for(var i = 0; i < arrowIDs.length; i++) {
+				var m = document.getElementById(arrowIDs[i]);
+				if(!excludeArrow || m != excludeArrow) {
+					dojo.html.removeClass(m, "pressed");
+				}
+			}
+		}
+
+		dojo.event.connect(document.documentElement, "onmousedown", function(e) {
+			if( dojo.html.hasClass(e.target, "downArrow") ) { return };
+			for(var i = 0; i < menuIDs.length; i++) {
+				if( dojo.dom.isDescendantOf(e.target, document.getElementById(menuIDs[i])) ) {
+					return;
+				}
+			}
+			hideAll();
+		});
+	}
+}
+dojo.inherits(dojo.widget.HtmlSimpleDropdownButtons, dojo.widget.HtmlWidget);


Property changes on: trunk/PhotoOp/root/static/js/src/widget/SimpleDropdownButtons.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/SlideShow.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/SlideShow.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/SlideShow.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,132 @@
+/*
+	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.SlideShow");
+dojo.provide("dojo.widget.html.SlideShow");
+
+dojo.require("dojo.event");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.lfx.*");
+dojo.require("dojo.style");
+
+dojo.widget.defineWidget(
+	"dojo.widget.html.SlideShow",
+	dojo.widget.HtmlWidget,
+	{
+		templatePath: dojo.uri.dojoUri("src/widget/templates/HtmlSlideShow.html"),
+		templateCssPath: dojo.uri.dojoUri("src/widget/templates/HtmlSlideShow.css"),
+
+		// over-ride some defaults
+		isContainer: false,
+		widgetType: "SlideShow",
+
+		// useful properties
+		imgUrls: [],		// the images we'll go through
+		imgUrlBase: "",
+		urlsIdx: 0,		// where in the images we are
+		delay: 4000, 		// give it 4 seconds
+		transitionInterval: 2000, // 2 seconds
+		imgWidth: 800,	// img width
+		imgHeight: 600,	// img height
+		background: "img2", // what's in the bg
+		foreground: "img1", // what's in the fg
+		stopped: false,	// should I stay or should I go?
+		fadeAnim: null, // references our animation
+
+		// our DOM nodes:
+		imagesContainer: null,
+		startStopButton: null,
+		controlsContainer: null,
+		img1: null,
+		img2: null,
+
+		fillInTemplate: function(){
+			dojo.style.setOpacity(this.img1, 0.9999);
+			dojo.style.setOpacity(this.img2, 0.9999);
+			with(this.imagesContainer.style){
+				width = this.imgWidth+"px";
+				height = this.imgHeight+"px";
+			}
+			with(this.img1.style){
+				width = this.imgWidth+"px";
+				height = this.imgHeight+"px";
+			}
+			with(this.img2.style){
+				width = this.imgWidth+"px";
+				height = this.imgHeight+"px";
+			}
+			if(this.imgUrls.length>1){
+				this.img2.src = this.imgUrlBase+this.imgUrls[this.urlsIdx++];
+				this.endTransition();
+			}else{
+				this.img1.src = this.imgUrlBase+this.imgUrls[this.urlsIdx++];
+			}
+		},
+
+		togglePaused: function(){
+			if(this.stopped){
+				this.stopped = false;
+				this.backgroundImageLoaded();
+				this.startStopButton.value= "pause";
+			}else{
+				this.stopped = true;
+				this.startStopButton.value= "play";
+			}
+		},
+
+		backgroundImageLoaded: function(){
+			// start fading out the foreground image
+			if(this.stopped){ return; }
+
+			// actually start the fadeOut effect
+			// NOTE: if we wanted to use other transition types, we'd set them up
+			// 		 here as well
+			if(this.fadeAnim) {
+				this.fadeAnim.stop();
+			}
+			this.fadeAnim = dojo.lfx.fadeOut(this[this.foreground], 
+				this.transitionInterval, null);
+			dojo.event.connect(this.fadeAnim,"onEnd",this,"endTransition");
+			this.fadeAnim.play();
+		},
+
+		endTransition: function(){
+			// move the foreground image to the background 
+			with(this[this.background].style){ zIndex = parseInt(zIndex)+1; }
+			with(this[this.foreground].style){ zIndex = parseInt(zIndex)-1; }
+
+			// fg/bg book-keeping
+			var tmp = this.foreground;
+			this.foreground = this.background;
+			this.background = tmp;
+
+			// keep on truckin
+			this.loadNextImage();
+		},
+
+		loadNextImage: function(){
+			// load a new image in that container, and make sure it informs
+			// us when it finishes loading
+			dojo.event.kwConnect({
+				srcObj: this[this.background],
+				srcFunc: "onload",
+				adviceObj: this,
+				adviceFunc: "backgroundImageLoaded",
+				once: true, // make sure we only ever hear about it once
+				delay: this.delay
+			});
+			dojo.style.setOpacity(this[this.background], 1.0);
+			this[this.background].src = this.imgUrlBase+this.imgUrls[this.urlsIdx++];
+			if(this.urlsIdx>(this.imgUrls.length-1)){
+				this.urlsIdx = 0;
+			}
+		}
+	}
+);


Property changes on: trunk/PhotoOp/root/static/js/src/widget/SlideShow.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/SortableTable.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/SortableTable.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/SortableTable.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,38 @@
+/*
+	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.SortableTable");
+dojo.require("dojo.widget.*");
+dojo.requireAfterIf("html", "dojo.widget.html.SortableTable");
+dojo.widget.tags.addParseTreeHandler("dojo:sortableTable");
+
+//	set up the general widget
+dojo.widget.SortableTable=function(){
+	//	summary
+	//	base class for the SortableTable
+	dojo.widget.Widget.call(this);
+	this.widgetType="SortableTable";
+	this.isContainer=false;
+
+	//	custom properties
+	this.enableMultipleSelect=false;
+	this.maximumNumberOfSelections=0;	//	0 for unlimited, is the default.
+	this.enableAlternateRows=false;
+	this.minRows=0;	//	0 means ignore.
+	this.defaultDateFormat="%D";
+	this.data=[];
+	this.selected=[];		//	always an array to handle multiple selections.
+	this.columns=[];
+	this.sortIndex=0;		//	index of the column sorted on, first is the default.
+	this.sortDirection=0;	//	0==asc, 1==desc
+	this.valueField="Id";	//	if a JSON structure is parsed and there is a field of this name,
+							//	a value attribute will be added to the row (tr value="{Id}")
+};
+dojo.inherits(dojo.widget.SortableTable, dojo.widget.Widget);


Property changes on: trunk/PhotoOp/root/static/js/src/widget/SortableTable.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/Spinner.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/Spinner.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/Spinner.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,194 @@
+/*
+	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.Spinner");
+dojo.provide("dojo.widget.AdjustableIntegerTextbox");
+
+dojo.require("dojo.widget.validate.IntegerTextbox");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.event.*");
+
+/*
+  ****** AdjustableIntegerTextbox ******
+
+  A subclass of IntegerTextbox.
+*/
+dojo.widget.AdjustableIntegerTextbox = function(node) {
+        // this property isn't a primitive and needs to be created on a per-item basis.
+        this.flags = {};
+}
+dojo.inherits(dojo.widget.AdjustableIntegerTextbox, dojo.widget.validate.IntegerTextbox);
+dojo.lang.extend(dojo.widget.AdjustableIntegerTextbox, {
+        // new subclass properties
+        widgetType: "AdjustableIntegerTextbox",
+		delta: "1",
+
+        adjustValue: function(direction, x){
+			var val = this.getValue().replace(/[^\-+\d]/g, "");
+			if(val.length == 0){ return; }
+
+			num = Math.min(Math.max((parseInt(val)+(parseInt(this.delta) * direction)), this.flags.min), this.flags.max);
+			val = (new Number(num)).toString();
+
+			if(num >= 0){
+				val = ((this.flags.signed == true)?'+':' ')+val; // make sure first char is a nondigit
+			}
+
+			if(this.flags.separator.length > 0){
+				for (var i=val.length-3; i > 1; i-=3){
+					val = val.substr(0,i)+this.flags.separator+val.substr(i);
+				}
+			}
+
+			if(val.substr(0,1) == ' '){ val = val.substr(1); } // remove space
+
+			this.setValue(val);
+
+			return val.length;
+	}
+});
+dojo.widget.tags.addParseTreeHandler("dojo:AdjustableIntegerTextbox");
+
+/*
+  ****** AdjustableRealNumberTextbox ******
+
+  A subclass of RealNumberTextbox.
+  @attr places    The exact number of decimal places.  If omitted, it's unlimited and optional.
+  @attr exponent  Can be true or false.  If omitted the exponential part is optional.
+  @attr eSigned   Is the exponent signed?  Can be true or false, if omitted the sign is optional.
+*/
+dojo.widget.AdjustableRealNumberTextbox = function(node) {
+        // this property isn't a primitive and needs to be created on a per-item basis.
+        this.flags = {};
+}
+dojo.inherits(dojo.widget.AdjustableRealNumberTextbox, dojo.widget.validate.RealNumberTextbox);
+dojo.lang.extend(dojo.widget.AdjustableRealNumberTextbox, {
+        // new subclass properties
+        widgetType: "AdjustableRealNumberTextbox",
+		delta: "1e1",
+
+        adjustValue: function(direction, x){
+			var val = this.getValue().replace(/[^\-+\.eE\d]/g, "");
+			if(!val.length){ return; }
+
+			var num = parseFloat(val);
+			if(isNaN(num)){ return; }
+			var delta = this.delta.split(/[eE]/);
+			if(!delta.length){
+				delta = [1, 1];
+			}else{
+				delta[0] = parseFloat(delta[0].replace(/[^\-+\.\d]/g, ""));
+				if(isNaN(delta[0])){ delta[0] = 1; }
+				if(delta.length > 1){
+					delta[1] = parseInt(delta[1]);
+				}
+				if(isNaN(delta[1])){ delta[1] = 1; }
+			}
+			val = this.getValue().split(/[eE]/);
+			if(!val.length){ return; }
+			var numBase = parseFloat(val[0].replace(/[^\-+\.\d]/g, ""));
+			if(val.length == 1){
+				var numExp = 0;
+			}else{
+				var numExp = parseInt(val[1].replace(/[^\-+\d]/g, ""));
+			}
+			if(x <= val[0].length){
+				x = 0;
+				numBase += delta[0] * direction;
+			}else{
+				x = Number.MAX_VALUE;
+				numExp += delta[1] * direction;
+				if(this.flags.eSigned == false && numExp < 0){
+					numExp = 0;
+				}
+			}
+			num = Math.min(Math.max((numBase * Math.pow(10,numExp)), this.flags.min), this.flags.max);
+			if((this.flags.exponent == true || (this.flags.exponent != false && x != 0)) && num.toExponential){
+				if (isNaN(this.flags.places) || this.flags.places == Infinity){
+					val = num.toExponential();
+				}else{
+					val = num.toExponential(this.flags.places);
+				}
+			}else if(num.toFixed && num.toPrecision){
+				if(isNaN(this.flags.places)){
+					val = num.toPrecision((1/3).toString().length-1);
+				}else{
+					val = num.toFixed(this.flags.places);
+				}
+			}else{
+				val = num.toString();
+			}
+
+			if(num >= 0){
+				if(this.flags.signed == true){
+					val = '+' + val;
+				}
+			}
+			val = val.split(/[eE]/);
+			if(this.flags.separator.length > 0){
+				if(num >= 0 && val[0].substr(0,1) != '+'){
+					val[0] = ' ' + val[0]; // make sure first char is nondigit for easy algorithm
+				}
+				var i = val[0].lastIndexOf('.');
+				if(i >= 0){
+					i -= 3;
+				}else{
+					i = val[0].length-3;
+				}
+				for (; i > 1; i-=3){
+					val[0] = val[0].substr(0,i)+this.flags.separator+val[0].substr(i);
+				}
+				if(val[0].substr(0,1) == ' '){ val[0] = val[0].substr(1); } // remove space
+			}
+			if(val.length > 1){
+				if((this.flags.eSigned == true)&&(val[1].substr(0,1) != '+')){
+					val[1] = '+' + val[1];
+				}else if((!this.flags.eSigned)&&(val[1].substr(0,1) == '+')){
+					val[1] = val[1].substr(1);
+				}else if((!this.flags.eSigned)&&(val[1].substr(0,1) == '-')&&(num.toFixed && num.toPrecision)){
+					if(isNaN(this.flags.places)){
+						val[0] = num.toPrecision((1/3).toString().length-1);
+					}else{
+						val[0] = num.toFixed(this.flags.places).toString();
+					}
+					val[1] = "0";
+				}
+				val[0] += 'e' + val[1];
+			}
+			this.setValue(val[0]);
+			if(x > val[0].length){ x = val[0].length; }
+			return x;
+	}
+});
+dojo.widget.tags.addParseTreeHandler("dojo:AdjustableRealNumberTextbox");
+
+dojo.widget.Spinner = function(){
+	dojo.widget.Widget.call(this);
+}
+
+dojo.inherits(dojo.widget.Spinner, dojo.widget.Widget);
+
+dojo.widget.Spinner.defaults = {
+	widgetType: "Spinner",
+	isContainer: false
+};
+
+dojo.lang.extend(dojo.widget.Spinner, dojo.widget.Spinner.defaults);
+
+dojo.widget.DomSpinner = function(){
+	dojo.widget.Spinner.call(this);
+	dojo.widget.DomWidget.call(this, true);
+}
+
+dojo.inherits(dojo.widget.DomSpinner, dojo.widget.DomWidget);
+dojo.widget.tags.addParseTreeHandler("dojo:Spinner");
+
+// render-specific includes
+dojo.requireAfterIf("html", "dojo.widget.html.Spinner");


Property changes on: trunk/PhotoOp/root/static/js/src/widget/Spinner.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/SplitContainer.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/SplitContainer.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/SplitContainer.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,579 @@
+/*
+	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.SplitContainer");
+dojo.provide("dojo.widget.SplitContainerPanel");
+dojo.provide("dojo.widget.html.SplitContainer");
+dojo.provide("dojo.widget.html.SplitContainerPanel");
+
+//
+// TODO
+// make it prettier
+// active dragging upwards doesn't always shift other bars (direction calculation is wrong in this case)
+//
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.LayoutContainer");
+dojo.require("dojo.widget.HtmlWidget");
+dojo.require("dojo.html");
+dojo.require("dojo.style");
+dojo.require("dojo.dom");
+dojo.require("dojo.io");	// workaround dojo bug. dojo.io.cookie requires dojo.io but it still doesn't get pulled in
+dojo.require("dojo.io.cookie");
+
+dojo.widget.html.SplitContainer = function(){
+
+	dojo.widget.HtmlWidget.call(this);
+
+	this.sizers = [];
+}
+
+dojo.inherits(dojo.widget.html.SplitContainer, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.html.SplitContainer, {
+	widgetType: "SplitContainer",
+	isContainer: true,
+
+	virtualSizer: null,
+	isHorizontal: 0,
+	paneBefore: null,
+	paneAfter: null,
+	isSizing: false,
+	dragOffset: null,
+	startPoint: null,
+	lastPoint: null,
+	sizingSplitter: null,
+	isActiveResize: 0,
+	offsetX: 0,
+	offsetY: 0,
+	isDraggingLeft: 0,
+	templateCssPath: dojo.uri.dojoUri("src/widget/templates/HtmlSplitContainer.css"),
+	originPos: null,
+	persist: true,		// save splitter positions in a cookie
+
+	activeSizing: '',
+	sizerWidth: 15,
+	orientation: 'horizontal',
+
+	debugName: '',
+
+	fillInTemplate: function(){
+
+		dojo.style.insertCssFile(this.templateCssPath, null, true);
+		dojo.html.addClass(this.domNode, "dojoSplitContainer");
+		this.domNode.style.overflow='hidden';	// workaround firefox bug
+
+		this.paneWidth = dojo.style.getContentWidth(this.domNode);
+		this.paneHeight = dojo.style.getContentHeight(this.domNode);
+
+		this.isHorizontal = (this.orientation == 'horizontal') ? 1 : 0;
+		this.isActiveResize = (this.activeSizing == '1') ? 1 : 0;
+
+		//dojo.debug("fillInTemplate for "+this.debugName);
+	},
+
+	onResized: function(e){
+		this.paneWidth = dojo.style.getContentWidth(this.domNode);
+		this.paneHeight = dojo.style.getContentHeight(this.domNode);
+		this.layoutPanels();
+	},
+
+	postCreate: function(args, fragment, parentComp){
+
+		// dojo.debug("post create for "+this.debugName);
+
+		// attach the children and create the draggers
+		for(var i=0; i<this.children.length; i++){
+            with(this.children[i].domNode.style){
+                position = "absolute";
+            }
+            dojo.html.addClass(this.children[i].domNode,
+                "dojoSplitPane");
+
+            if(i == this.children.length-1){
+                break;
+            }
+
+            this._addSizer();
+		}
+
+		// create the fake dragger
+		this.virtualSizer = document.createElement('div');
+		this.virtualSizer.style.position = 'absolute';
+		this.virtualSizer.style.display = 'none';
+		//this.virtualSizer.style.backgroundColor = 'lime';
+		this.virtualSizer.style.zIndex = 10;
+		this.virtualSizer.className = this.isHorizontal ? 'dojoSplitContainerVirtualSizerH' : 'dojoSplitContainerVirtualSizerV';
+		this.domNode.appendChild(this.virtualSizer);
+
+		dojo.html.disableSelection(this.virtualSizer);
+
+		if(this.persist){
+			this.restoreState();
+		}
+
+		// size the panels once the browser has caught up
+		this.resizeSoon();
+	},
+
+    _injectChild: function(child) {
+        with(child.domNode.style){
+            position = "absolute";
+        }
+        dojo.html.addClass(child.domNode,
+            "dojoSplitPane");
+    },
+
+    _addSizer: function() {
+        var i = this.sizers.length;
+
+        this.sizers[i] = document.createElement('div');
+        this.sizers[i].style.position = 'absolute';
+        this.sizers[i].className = this.isHorizontal ? 'dojoSplitContainerSizerH' : 'dojoSplitContainerSizerV';
+
+        var self = this;
+        var handler = (function(){ var sizer_i = i; return function(e){ self.beginSizing(e, sizer_i); } })();
+        dojo.event.connect(this.sizers[i], "onmousedown", handler);
+
+        this.domNode.appendChild(this.sizers[i]);
+        dojo.html.disableSelection(this.sizers[i]);
+    },
+
+    removeChild: function(widget){
+        // Remove sizer, but only if widget is really our child and
+        // we have at least one sizer to throw away
+        if (this.sizers.length > 0) {
+            for(var x=0; x<this.children.length; x++){
+                if(this.children[x] === widget){
+                    var i = this.sizers.length - 1;
+                    this.domNode.removeChild(this.sizers[i]);
+                    this.sizers.length = i;
+                    break;
+                }
+            }
+        }
+
+        // Remove widget and repaint
+        dojo.widget.html.SplitContainer.superclass.removeChild.call(this, widget, arguments);
+        this.onResized();
+    },
+
+    addChild: function(widget, overrideContainerNode, pos, ref, insertIndex){
+        dojo.widget.html.SplitContainer.superclass.addChild.call(this, widget, overrideContainerNode, pos, ref, insertIndex);
+        this._injectChild(widget);
+
+        if (this.children.length > 1) {
+            this._addSizer();
+        }
+
+        this.layoutPanels();
+    },
+
+    layoutPanels: function(){
+        if (this.children.length == 0){ return; }
+
+		//
+		// calculate space
+		//
+
+		var space = this.isHorizontal ? this.paneWidth : this.paneHeight;
+
+		if (this.children.length > 1){
+
+			space -= this.sizerWidth * (this.children.length - 1);
+		}
+
+
+		//
+		// calculate total of SizeShare values
+		//
+
+		var out_of = 0;
+
+		for(var i=0; i<this.children.length; i++){
+
+			out_of += this.children[i].sizeShare;
+		}
+
+
+		//
+		// work out actual pixels per sizeshare unit
+		//
+
+		var pix_per_unit = space / out_of;
+
+
+		//
+		// set the SizeActual member of each pane
+		//
+
+		var total_size = 0;
+
+		for(var i=0; i<this.children.length-1; i++){
+
+			var size = Math.round(pix_per_unit * this.children[i].sizeShare);
+			this.children[i].sizeActual = size;
+			total_size += size;
+		}
+		this.children[this.children.length-1].sizeActual = space - total_size;
+
+		//
+		// make sure the sizes are ok
+		//
+
+		this.checkSizes();
+
+
+		//
+		// now loop, positioning each pane and letting children resize themselves
+		//
+
+		var pos = 0;
+		var size = this.children[0].sizeActual;
+		this.movePanel(this.children[0].domNode, pos, size);
+		this.children[0].position = pos;
+        this.children[0].checkSize();
+		pos += size;
+
+		for(var i=1; i<this.children.length; i++){
+
+			// first we position the sizing handle before this pane
+			this.movePanel(this.sizers[i-1], pos, this.sizerWidth);
+			this.sizers[i-1].position = pos;
+			pos += this.sizerWidth;
+
+			size = this.children[i].sizeActual;
+			this.movePanel(this.children[i].domNode, pos, size);
+			this.children[i].position = pos;
+            this.children[i].checkSize();
+			pos += size;
+		}
+	},
+
+	movePanel: function(panel, pos, size){
+		if (this.isHorizontal){
+			panel.style.left = pos + 'px';
+			panel.style.top = 0;
+
+			dojo.style.setOuterWidth(panel, size);
+			dojo.style.setOuterHeight(panel, this.paneHeight);
+		}else{
+			panel.style.left = 0;
+			panel.style.top = pos + 'px';
+
+			dojo.style.setOuterWidth(panel, this.paneWidth);
+			dojo.style.setOuterHeight(panel, size);
+		}
+	},
+
+	growPane: function(growth, pane){
+
+		if (growth > 0){
+			if (pane.sizeActual > pane.sizeMin){
+				if ((pane.sizeActual - pane.sizeMin) > growth){
+
+					// stick all the growth in this pane
+					pane.sizeActual = pane.sizeActual - growth;
+					growth = 0;
+				}else{
+					// put as much growth in here as we can
+					growth -= pane.sizeActual - pane.sizeMin;
+					pane.sizeActual = pane.sizeMin;
+				}
+			}
+		}
+		return growth;
+	},
+
+	checkSizes: function(){
+
+		var total_min_size = 0;
+		var total_size = 0;
+
+		for(var i=0; i<this.children.length; i++){
+
+			total_size += this.children[i].sizeActual;
+			total_min_size += this.children[i].sizeMin;
+		}
+
+		// only make adjustments if we have enough space for all the minimums
+
+		if (total_min_size <= total_size){
+
+			var growth = 0;
+
+			for(var i=0; i<this.children.length; i++){
+
+				if (this.children[i].sizeActual < this.children[i].sizeMin){
+
+					growth += this.children[i].sizeMin - this.children[i].sizeActual;
+					this.children[i].sizeActual = this.children[i].sizeMin;
+				}
+			}
+
+			if (growth > 0){
+				if (this.isDraggingLeft){
+					for(var i=this.children.length-1; i>=0; i--){
+						growth = this.growPane(growth, this.children[i]);
+					}
+				}else{
+					for(var i=0; i<this.children.length; i++){
+						growth = this.growPane(growth, this.children[i]);
+					}
+				}
+			}
+		}else{
+
+			for(var i=0; i<this.children.length; i++){
+				this.children[i].sizeActual = Math.round(total_size * (this.children[i].sizeMin / total_min_size));
+			}
+		}
+	},
+
+	beginSizing: function(e, i){
+		var clientX = e.layerX;
+		var clientY = e.layerY;
+		var screenX = e.pageX;
+		var screenY = e.pageY;
+
+		this.paneBefore = this.children[i];
+		this.paneAfter = this.children[i+1];
+
+		this.isSizing = true;
+		this.sizingSplitter = this.sizers[i];
+		this.originPos = dojo.style.getAbsolutePosition(this.domNode, true);
+		this.dragOffset = {'x':clientX, 'y':clientY};
+		this.startPoint  = {'x':screenX, 'y':screenY};
+		this.lastPoint  = {'x':screenX, 'y':screenY};
+
+		this.offsetX = screenX - clientX;
+		this.offsetY = screenY - clientY;
+
+		if (!this.isActiveResize){
+			this.showSizingLine();
+		}
+
+		//
+		// attach mouse events
+		//
+
+		dojo.event.connect(document.documentElement, "onmousemove", this, "changeSizing");
+		dojo.event.connect(document.documentElement, "onmouseup", this, "endSizing");
+	},
+
+	changeSizing: function(e){
+		var screenX = e.pageX;
+		var screenY = e.pageY;
+
+		if (this.isActiveResize){
+			this.lastPoint = {'x':screenX, 'y':screenY};
+			this.movePoint();
+			this.updateSize();
+		}else{
+			this.lastPoint = {'x':screenX, 'y':screenY};
+			this.movePoint();
+			this.moveSizingLine();
+		}
+	},
+
+	endSizing: function(e){
+
+		if (!this.isActiveResize){
+			this.hideSizingLine();
+		}
+
+		this.updateSize();
+
+		this.isSizing = false;
+
+		dojo.event.disconnect(document.documentElement, "onmousemove", this, "changeSizing");
+		dojo.event.disconnect(document.documentElement, "onmouseup", this, "endSizing");
+		
+		if(this.persist){
+			this.saveState(this);
+		}
+	},
+
+	movePoint: function(){
+
+		// make sure FLastPoint is a legal point to drag to
+		var p = this.screenToMainClient(this.lastPoint);
+
+		if (this.isHorizontal){
+
+			var a = p.x - this.dragOffset.x;
+			a = this.legaliseSplitPoint(a);
+			p.x = a + this.dragOffset.x;
+		}else{
+			var a = p.y - this.dragOffset.y;
+			a = this.legaliseSplitPoint(a);
+			p.y = a + this.dragOffset.y;
+		}
+
+		this.lastPoint = this.mainClientToScreen(p);
+	},
+
+	screenToClient: function(pt){
+
+		pt.x -= (this.offsetX + this.sizingSplitter.position);
+		pt.y -= (this.offsetY + this.sizingSplitter.position);
+
+		return pt;
+	},
+
+	clientToScreen: function(pt){
+
+		pt.x += (this.offsetX + this.sizingSplitter.position);
+		pt.y += (this.offsetY + this.sizingSplitter.position);
+
+		return pt;
+	},
+
+	screenToMainClient: function(pt){
+
+		pt.x -= this.offsetX;
+		pt.y -= this.offsetY;
+
+		return pt;
+	},
+
+	mainClientToScreen: function(pt){
+
+		pt.x += this.offsetX;
+		pt.y += this.offsetY;
+
+		return pt;
+	},
+
+	legaliseSplitPoint: function(a){
+
+		a += this.sizingSplitter.position;
+
+		this.isDraggingLeft = (a > 0) ? 1 : 0;
+
+		if (!this.isActiveResize){
+
+			if (a < this.paneBefore.position + this.paneBefore.sizeMin){
+
+				a = this.paneBefore.position + this.paneBefore.sizeMin;
+			}
+
+			if (a > this.paneAfter.position + (this.paneAfter.sizeActual - (this.sizerWidth + this.paneAfter.sizeMin))){
+
+				a = this.paneAfter.position + (this.paneAfter.sizeActual - (this.sizerWidth + this.paneAfter.sizeMin));
+			}
+		}
+
+		a -= this.sizingSplitter.position;
+
+		this.checkSizes();
+
+		return a;
+	},
+
+	updateSize: function(){
+
+		var p = this.clientToScreen(this.lastPoint);
+		var p = this.screenToClient(this.lastPoint);
+
+		var pos = this.isHorizontal ? p.x - (this.dragOffset.x + this.originPos.x) : p.y - (this.dragOffset.y + this.originPos.y);
+
+		var start_region = this.paneBefore.position;
+		var end_region   = this.paneAfter.position + this.paneAfter.sizeActual;
+
+		this.paneBefore.sizeActual = pos - start_region;
+		this.paneAfter.position    = pos + this.sizerWidth;
+		this.paneAfter.sizeActual  = end_region - this.paneAfter.position;
+
+		for(var i=0; i<this.children.length; i++){
+
+			this.children[i].sizeShare = this.children[i].sizeActual;
+		}
+
+		this.layoutPanels();
+	},
+
+	showSizingLine: function(){
+
+		this.moveSizingLine();
+
+		if (this.isHorizontal){
+			dojo.style.setOuterWidth(this.virtualSizer, this.sizerWidth);
+			dojo.style.setOuterHeight(this.virtualSizer, this.paneHeight);
+		}else{
+			dojo.style.setOuterWidth(this.virtualSizer, this.paneWidth);
+			dojo.style.setOuterHeight(this.virtualSizer, this.sizerWidth);
+		}
+
+		this.virtualSizer.style.display = 'block';
+	},
+
+	hideSizingLine: function(){
+
+		this.virtualSizer.style.display = 'none';
+	},
+
+	moveSizingLine: function(){
+
+		var origin = {'x':0, 'y':0};
+
+		if (this.isHorizontal){
+			origin.x += (this.lastPoint.x - this.startPoint.x) + this.sizingSplitter.position;
+		}else{
+			origin.y += (this.lastPoint.y - this.startPoint.y) + this.sizingSplitter.position;
+		}
+
+		this.virtualSizer.style.left = origin.x + 'px';
+		this.virtualSizer.style.top = origin.y + 'px';
+	},
+	
+	_getCookieName: function(i) {
+		return this.widgetId + "_" + i;
+	},
+
+	restoreState: function () {
+		for(var i=0; i<this.children.length; i++) {
+			var cookieName = this._getCookieName(i);
+			var cookieValue = dojo.io.cookie.getCookie(cookieName);
+			if (cookieValue != null) {
+				var pos = parseInt(cookieValue);
+				this.children[i].sizeShare=pos;
+			}
+		}
+	},
+
+	saveState: function (){
+		for(var i=0; i<this.children.length; i++) {
+			var cookieName = this._getCookieName(i);
+			dojo.io.cookie.setCookie(cookieName, this.children[i].sizeShare, null, null, null, null);
+		}
+	}
+});
+
+// These arguments can be specified for the children of a SplitContainer.
+// Since any widget can be specified as a SplitContainer child, mix them
+// into the base widget class.  (This is a hack, but it's effective.)
+dojo.lang.extend(dojo.widget.Widget, {
+	sizeMin: 10,
+	sizeShare: 10
+});
+
+// Deprecated class for split pane children.
+// Actually any widget can be the child of a split pane
+dojo.widget.html.SplitContainerPanel = function(){
+	dojo.widget.html.LayoutContainer.call(this);
+}
+dojo.inherits(dojo.widget.html.SplitContainerPanel, dojo.widget.html.LayoutContainer);
+dojo.lang.extend(dojo.widget.html.SplitContainerPanel, {
+	widgetType: "SplitContainerPanel"
+});
+
+dojo.widget.tags.addParseTreeHandler("dojo:SplitContainer");
+dojo.widget.tags.addParseTreeHandler("dojo:SplitContainerPanel");


Property changes on: trunk/PhotoOp/root/static/js/src/widget/SplitContainer.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/SvgButton.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/SvgButton.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/SvgButton.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,141 @@
+/*
+	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
+*/
+
+// FIXME: not yet functional
+
+dojo.provide("dojo.widget.SvgButton");
+
+dojo.require("dojo.widget.Button");
+
+dojo.widget.SvgButton = function(){
+	// FIXME: this is incomplete and doesn't work yet
+	// if DOMButton turns into a mixin, we should subclass Button instead and
+	// just mix in the DOMButton properties.
+
+	dojo.widget.DomButton.call(this);
+	dojo.widget.SvgWidget.call(this);
+
+	// FIXME: freaking implement this already!
+	this.onFoo = function(){ alert("bar"); }
+
+	this.label = "huzzah!";
+
+	this.setLabel = function(x, y, textSize, label, shape){
+		//var labelNode = this.domNode.ownerDocument.createTextNode(this.label);
+		//var textNode = this.domNode.ownerDocument.createElement("text");
+		var coords = dojo.widget.SvgButton.prototype.coordinates(x, y, textSize, label, shape);
+		var textString = "";
+		switch(shape) {
+			case "ellipse":
+				textString = "<text x='"+ coords[6] + "' y='"+ coords[7] + "'>"+ label + "</text>";
+				//textNode.setAttribute("x", coords[6]);
+				//textNode.setAttribute("y", coords[7]);
+				break;
+			case "rectangle":
+				//FIXME: implement
+				textString = "";
+				//textNode.setAttribute("x", coords[6]);
+				//textNode.setAttribute("y", coords[7]);
+				break;
+			case "circle":
+				//FIXME: implement
+				textString = "";
+				//textNode.setAttribute("x", coords[6]);
+				//textNode.setAttribute("y", coords[7]);
+				break;
+		}
+		//textNode.appendChild(labelNode);
+		//this.domNode.appendChild(textNode);
+		return textString;
+		//alert(textNode.getComputedTextLength());
+	}
+
+	this.fillInTemplate = function(x, y, textSize, label, shape){
+		// the idea is to set the text to the appropriate place given its length
+		// and the template shape
+		
+		// FIXME: For now, assuming text sizes are integers in SVG units
+		this.textSize = textSize || 12;
+		this.label = label;
+		// FIXEME: for now, I'm going to fake this... need to come up with a real way to 
+		// determine the actual width of the text, such as computedStyle
+		var textWidth = this.label.length*this.textSize ;
+		//this.setLabel();
+	}
+}
+
+dojo.inherits(dojo.widget.SvgButton, dojo.widget.DomButton);
+
+// FIXME
+dojo.widget.SvgButton.prototype.shapeString = function(x, y, textSize, label, shape) {
+	switch(shape) {
+		case "ellipse":
+			var coords = dojo.widget.SvgButton.prototype.coordinates(x, y, textSize, label, shape)
+			return "<ellipse cx='"+ coords[4]+"' cy='"+ coords[5]+"' rx='"+ coords[2]+"' ry='"+ coords[3]+"'/>";
+			break;
+		case "rect":
+			//FIXME: implement
+			return "";
+			//return "<rect x='110' y='45' width='70' height='30'/>";
+			break;
+		case "circle":
+			//FIXME: implement
+			return "";
+			//return "<circle cx='210' cy='60' r='23'/>";
+			break;
+	}
+}
+
+dojo.widget.SvgButton.prototype.coordinates = function(x, y, textSize, label, shape) {
+	switch(shape) {
+		case "ellipse":
+			var buttonWidth = label.length*textSize;
+			var buttonHeight = textSize*2.5
+			var rx = buttonWidth/2;
+			var ry = buttonHeight/2;
+			var cx = rx + x;
+			var cy = ry + y;
+			var textX = cx - rx*textSize/25;
+			var textY = cy*1.1;
+			return [buttonWidth, buttonHeight, rx, ry, cx, cy, textX, textY];
+			break;
+		case "rectangle":
+			//FIXME: implement
+			return "";
+			break;
+		case "circle":
+			//FIXME: implement
+			return "";
+			break;
+	}
+}
+
+dojo.widget.SvgButton.prototype.labelString = function(x, y, textSize, label, shape){
+	var textString = "";
+	var coords = dojo.widget.SvgButton.prototype.coordinates(x, y, textSize, label, shape);
+	switch(shape) {
+		case "ellipse":
+			textString = "<text x='"+ coords[6] + "' y='"+ coords[7] + "'>"+ label + "</text>";
+			break;
+		case "rectangle":
+			//FIXME: implement
+			textString = "";
+			break;
+		case "circle":
+			//FIXME: implement
+			textString = "";
+			break;
+	}
+	return textString;
+}
+
+dojo.widget.SvgButton.prototype.templateString = function(x, y, textSize, label, shape) {
+	return "<g class='dojoButton' dojoAttachEvent='onClick; onMouseMove: onFoo;' dojoAttachPoint='labelNode'>"+ dojo.widgets.SVGButton.prototype.shapeString(x, y, textSize, label, shape) + dojo.widget.SVGButton.prototype.labelString(x, y, textSize, label, shape) + "</g>";
+}


Property changes on: trunk/PhotoOp/root/static/js/src/widget/SvgButton.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/SvgWidget.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/SvgWidget.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/SvgWidget.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,92 @@
+/*
+	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.widget.DomWidget");
+dojo.provide("dojo.widget.SvgWidget");
+dojo.provide("dojo.widget.SVGWidget"); // back compat
+
+dojo.require("dojo.dom");
+
+// SVGWidget is a mixin ONLY
+dojo.widget.SvgWidget = function(args){
+	// mix in the parent type
+	// dojo.widget.DomWidget.call(this);
+}
+dojo.inherits(dojo.widget.SvgWidget, dojo.widget.DomWidget);
+
+dojo.lang.extend(dojo.widget.SvgWidget, {
+	getContainerHeight: function(){
+		// NOTE: container height must be returned as the INNER height
+		dojo.unimplemented("dojo.widget.SvgWidget.getContainerHeight");
+	},
+
+	getContainerWidth: function(){
+		// return this.parent.domNode.offsetWidth;
+		dojo.unimplemented("dojo.widget.SvgWidget.getContainerWidth");
+	},
+
+	setNativeHeight: function(height){
+		// var ch = this.getContainerHeight();
+		dojo.unimplemented("dojo.widget.SVGWidget.setNativeHeight");
+	},
+
+	createNodesFromText: function(txt, wrap){
+		return dojo.dom.createNodesFromText(txt, wrap);
+	}
+});
+
+dojo.widget.SVGWidget = dojo.widget.SvgWidget;
+
+try{
+(function(){
+	var tf = function(){
+		// FIXME: fill this in!!!
+		var rw = new function(){
+			dojo.widget.SvgWidget.call(this);
+			this.buildRendering = function(){ return; }
+			this.destroyRendering = function(){ return; }
+			this.postInitialize = function(){ return; }
+			this.cleanUp = function(){ return; }
+			this.widgetType = "SVGRootWidget";
+			this.domNode = document.documentElement;
+		}
+		var wm = dojo.widget.manager;
+		wm.root = rw;
+		wm.add(rw);
+
+		// extend the widgetManager with a getWidgetFromNode method
+		wm.getWidgetFromNode = function(node){
+			var filter = function(x){
+				if(x.domNode == node){
+					return true;
+				}
+			}
+			var widgets = [];
+			while((node)&&(widgets.length < 1)){
+				widgets = this.getWidgetsByFilter(filter);
+				node = node.parentNode;
+			}
+			if(widgets.length > 0){
+				return widgets[0];
+			}else{
+				return null;
+			}
+		}
+
+		wm.getWidgetFromEvent = function(domEvt){
+			return this.getWidgetFromNode(domEvt.target);
+		}
+
+		wm.getWidgetFromPrimitive = wm.getWidgetFromNode;
+	}
+	// make sure we get called when the time is right
+	dojo.event.connect(dojo.hostenv, "loaded", tf);
+})();
+}catch(e){ alert(e); }


Property changes on: trunk/PhotoOp/root/static/js/src/widget/SvgWidget.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/SwtWidget.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/SwtWidget.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/SwtWidget.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,64 @@
+/*
+	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.SwtWidget");
+
+dojo.require("dojo.event.*");
+dojo.require("dojo.widget.Widget");
+dojo.require("dojo.uri.*");
+dojo.require("dojo.lang.func");
+dojo.require("dojo.lang.extras");
+
+try{
+	importPackage(Packages.org.eclipse.swt.widgets);
+
+	dojo.declare("dojo.widget.SwtWidget", dojo.widget.Widget, {
+		initializer: function() {
+			if((arguments.length>0)&&(typeof arguments[0] == "object")){
+				this.create(arguments[0]);
+			}
+		},
+
+		display: null,
+		shell: null,
+
+		show: function(){ },
+		hide: function(){ },
+
+		addChild: function(){ },
+		registerChild: function(){ },
+		addWidgetAsDirectChild: function(){ },
+		removeChild: function(){ },
+		cleanUp: function(){ },
+		destroyRendering: function(){ },
+		postInitialize: function(){ },
+	});
+
+	// initialize SWT runtime
+
+	dojo.widget.SwtWidget.prototype.display = new Display();
+	dojo.widget.SwtWidget.prototype.shell = new Shell(dojo.widget.SwtWidget.prototype.display);
+
+	dojo.widget.manager.startShell = function(){
+		var sh = dojo.widget.SwtWidget.prototype.shell;
+		var d = dojo.widget.SwtWidget.prototype.display;
+		sh.open();
+		while(!sh.isDisposed()){
+			dojo.widget.manager.doNext();
+			if(!d.readAndDispatch()){
+				d.sleep();
+			}
+		}
+		d.dispose();
+	};
+}catch(e){
+	// seems we didn't have the SWT classes in the environment. Log it.
+	dojo.debug("dojo.widget.SwtWidget not loaded. SWT classes not available");
+}


Property changes on: trunk/PhotoOp/root/static/js/src/widget/SwtWidget.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/TabContainer.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/TabContainer.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/TabContainer.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,261 @@
+/*
+	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.TabContainer");
+dojo.provide("dojo.widget.html.TabContainer");
+dojo.provide("dojo.widget.Tab");
+
+dojo.require("dojo.lang.func");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.HtmlWidget");
+dojo.require("dojo.event.*");
+dojo.require("dojo.html");
+dojo.require("dojo.style");
+dojo.require("dojo.html.layout");
+
+//////////////////////////////////////////
+// TabContainer -- a set of Tabs
+//////////////////////////////////////////
+dojo.widget.html.TabContainer = function() {
+	dojo.widget.HtmlWidget.call(this);
+}
+
+dojo.inherits(dojo.widget.html.TabContainer, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.html.TabContainer, {
+	widgetType: "TabContainer",
+    isContainer: true,
+
+	// Constructor arguments
+	labelPosition: "top",
+	closeButton: "none",
+
+	useVisibility: false,		// true-->use visibility:hidden instead of display:none
+	
+	// if false, TabContainers size changes according to size of currently selected tab
+	doLayout: true,
+
+	templatePath: dojo.uri.dojoUri("src/widget/templates/HtmlTabContainer.html"),
+	templateCssPath: dojo.uri.dojoUri("src/widget/templates/HtmlTabContainer.css"),
+
+	selectedTab: "",		// initially selected tab (widgetId)
+
+	fillInTemplate: function(args, frag) {
+		// Copy style info from input node to output node
+		var source = this.getFragNodeRef(frag);
+		dojo.html.copyStyle(this.domNode, source);
+
+		dojo.widget.html.TabContainer.superclass.fillInTemplate.call(this, args, frag);
+	},
+
+	postCreate: function(args, frag) {
+		// Load all the tabs, creating a label for each one
+		for(var i=0; i<this.children.length; i++){
+			this._setupTab(this.children[i]);
+		}
+
+		if (this.closeButton=="pane") {
+			var div = document.createElement("div");
+			dojo.html.addClass(div, "dojoTabPanePaneClose");
+			var self = this;
+			dojo.event.connect(div, "onclick", function(){ self._runOnCloseTab(self.selectedTabWidget); });
+			dojo.event.connect(div, "onmouseover", function(){ dojo.html.addClass(div, "dojoTabPanePaneCloseHover"); });
+			dojo.event.connect(div, "onmouseout", function(){ dojo.html.removeClass(div, "dojoTabPanePaneCloseHover"); });
+			this.dojoTabLabels.appendChild(div);
+		}
+
+		if(this.doLayout){
+			dojo.html.addClass(this.dojoTabLabels, "dojoTabLabels-"+this.labelPosition);
+		} else {
+			dojo.html.addClass(this.dojoTabLabels, "dojoTabLabels-"+this.labelPosition+"-noLayout");
+		}
+
+        this._doSizing();
+
+		// Display the selected tab
+		if(this.selectedTabWidget){
+			this.selectTab(this.selectedTabWidget, true);
+		}
+	},
+
+	addChild: function(child, overrideContainerNode, pos, ref, insertIndex){
+		this._setupTab(child);
+		dojo.widget.html.TabContainer.superclass.addChild.call(this,child, overrideContainerNode, pos, ref, insertIndex);
+
+		// in case the tab labels have overflowed from one line to two lines
+		this._doSizing();
+	},
+
+	_setupTab: function(tab){
+		tab.domNode.style.display="none";
+
+		// Create label
+		tab.div = document.createElement("div");
+		dojo.widget.wai.setAttr(tab.div, "waiRole", "tab");
+		dojo.html.addClass(tab.div, "dojoTabPaneTab");
+		var span = document.createElement("span");
+		span.innerHTML = tab.label;
+		dojo.html.disableSelection(span);
+		if (this.closeButton=="tab") {
+			var img = document.createElement("div");
+			dojo.html.addClass(img, "dojoTabPaneTabClose");
+			var self = this;
+			dojo.event.connect(img, "onclick", function(evt){ self._runOnCloseTab(tab); dojo.event.browser.stopEvent(evt); });
+			dojo.event.connect(img, "onmouseover", function(){ dojo.html.addClass(img,"dojoTabPaneTabCloseHover"); });
+			dojo.event.connect(img, "onmouseout", function(){ dojo.html.removeClass(img,"dojoTabPaneTabCloseHover"); });
+			span.appendChild(img);
+		}
+		tab.div.appendChild(span);
+		this.dojoTabLabels.appendChild(tab.div);
+		
+		var self = this;
+		dojo.event.connect(tab.div, "onclick", function(){ self.selectTab(tab); });
+
+		if(!this.selectedTabWidget || this.selectedTab==tab.widgetId || tab.selected){
+    		this.selectedTabWidget = tab;
+        } else {
+            this._hideTab(tab);
+        }
+
+		dojo.html.addClass(tab.domNode, "dojoTabPane");
+		with(tab.domNode.style){
+			top = dojo.style.getPixelValue(this.containerNode, "padding-top", true);
+			left = dojo.style.getPixelValue(this.containerNode, "padding-left", true);
+		}
+	},
+
+	// Configure the content pane to take up all the space except for where the tab labels are
+	_doSizing: function(){
+		// position the labels and the container node
+		var labelAlign=this.labelPosition.replace(/-h/,"");
+		var children = [
+			{domNode: this.dojoTabLabels, layoutAlign: labelAlign},
+			{domNode: this.containerNode, layoutAlign: "client"}
+		];
+
+
+		if (this.doLayout) {
+			dojo.html.layout(this.domNode, children);
+		} 
+			
+		// size the current tab
+		// TODO: should have ptr to current tab rather than searching
+		var cw=dojo.style.getContentWidth(this.containerNode);
+		var ch=dojo.style.getContentHeight(this.containerNode);
+		dojo.lang.forEach(this.children, function(child){
+			//if (this.doLayout) {
+				if(child.selected){
+					child.resizeTo(cw, ch);
+				} 
+			//} else {
+			//	child.onResized();
+			//}
+		});
+		
+	},
+
+    removeChild: function(tab) {
+
+		// remove tab event handlers
+		dojo.event.disconnect(tab.div, "onclick", function () { });
+		if (this.closeButton=="tab") {
+			var img = tab.div.lastChild.lastChild;
+			if (img) {
+				dojo.html.removeClass(img, "dojoTabPaneTabClose", function () { });
+				dojo.event.disconnect(img, "onclick", function () { });
+				dojo.event.disconnect(img, "onmouseover", function () { });
+				dojo.event.disconnect(img, "onmouseout", function () { });
+			}
+		}
+
+        dojo.widget.html.TabContainer.superclass.removeChild.call(this, tab);
+
+        dojo.html.removeClass(tab.domNode, "dojoTabPane");
+        this.dojoTabLabels.removeChild(tab.div);
+        delete(tab.div);
+
+        if (this.selectedTabWidget === tab) {
+            this.selectedTabWidget = undefined;
+            if (this.children.length > 0) {
+                this.selectTab(this.children[0], true);
+            }
+        }
+
+		// in case the tab labels have overflowed from one line to two lines
+		this._doSizing();
+    },
+
+    selectTab: function(tab, _noRefresh) {
+		// Deselect old tab and select new one
+		if (this.selectedTabWidget) {
+			this._hideTab(this.selectedTabWidget);
+		}
+		this.selectedTabWidget = tab;
+		this._showTab(tab, _noRefresh);
+	},
+
+	_showTab: function(tab, _noRefresh) {
+		dojo.html.addClass(tab.div, "current");
+		tab.selected=true;
+		if ( this.useVisibility && !dojo.render.html.ie ) {
+			tab.domNode.style.visibility="visible";
+		} else {
+			// make sure we dont refresh onClose and on postCreate
+			// speeds up things a bit when using refreshOnShow and fixes #646
+			if(_noRefresh && tab.refreshOnShow){
+				var tmp = tab.refreshOnShow;
+				tab.refreshOnShow = false;
+				tab.show();
+				tab.refreshOnShow = tmp;
+			}else{
+				tab.show();
+			}
+
+			tab.resizeTo(
+				dojo.style.getContentWidth(this.containerNode),
+				dojo.style.getContentHeight(this.containerNode)
+			);
+		}
+	},
+
+	_hideTab: function(tab) {
+		dojo.html.removeClass(tab.div, "current");
+		tab.selected=false;
+		if( this.useVisibility ){
+			tab.domNode.style.visibility="hidden";
+		}else{
+			tab.hide();
+		}
+	},
+
+	_runOnCloseTab: function(tab) {
+		var onc = tab.extraArgs.onClose || tab.extraArgs.onclose;
+		var fcn = dojo.lang.isFunction(onc) ? onc : window[onc];
+		var remove = dojo.lang.isFunction(fcn) ? fcn(this,tab) : true;
+		if(remove) {
+			this.removeChild(tab);
+			// makes sure we can clean up executeScripts in ContentPane onUnLoad
+			tab.destroy();
+		}
+	},
+
+	onResized: function() {
+		this._doSizing();
+	}
+});
+dojo.widget.tags.addParseTreeHandler("dojo:TabContainer");
+
+// These arguments can be specified for the children of a TabContainer.
+// Since any widget can be specified as a TabContainer child, mix them
+// into the base widget class.  (This is a hack, but it's effective.)
+dojo.lang.extend(dojo.widget.Widget, {
+	label: "",
+	selected: false	// is this tab currently selected?
+});


Property changes on: trunk/PhotoOp/root/static/js/src/widget/TabContainer.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/TaskBar.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/TaskBar.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/TaskBar.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,32 @@
+/*
+	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.TaskBar");
+dojo.provide("dojo.widget.TaskBarItem");
+dojo.require("dojo.widget.Widget");
+
+dojo.widget.TaskBar = function(){
+	dojo.widget.Widget.call(this);
+
+	this.widgetType = "TaskBar";
+	this.isContainer = true;
+}
+dojo.inherits(dojo.widget.TaskBar, dojo.widget.Widget);
+dojo.widget.tags.addParseTreeHandler("dojo:taskbar");
+
+dojo.widget.TaskBarItem = function(){
+	dojo.widget.Widget.call(this);
+
+	this.widgetType = "TaskBarItem";
+}
+dojo.inherits(dojo.widget.TaskBarItem, dojo.widget.Widget);
+dojo.widget.tags.addParseTreeHandler("dojo:taskbaritem");
+
+dojo.requireAfterIf("html", "dojo.widget.html.TaskBar");


Property changes on: trunk/PhotoOp/root/static/js/src/widget/TaskBar.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/TimePicker.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/TimePicker.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/TimePicker.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,82 @@
+/*
+	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.TimePicker");
+dojo.provide("dojo.widget.TimePicker.util");
+dojo.require("dojo.widget.DomWidget");
+dojo.require("dojo.date");
+
+dojo.widget.TimePicker = function(){
+	dojo.widget.Widget.call(this);
+	this.widgetType = "TimePicker";
+	this.isContainer = false;
+	// the following aliases prevent breaking people using 0.2.x
+	this.toRfcDateTime = dojo.widget.TimePicker.util.toRfcDateTime;
+	this.fromRfcDateTime = dojo.widget.TimePicker.util.fromRfcDateTime;
+	this.toAmPmHour = dojo.widget.TimePicker.util.toAmPmHour;
+	this.fromAmPmHour = dojo.widget.TimePicker.util.fromAmPmHour;
+}
+
+dojo.inherits(dojo.widget.TimePicker, dojo.widget.Widget);
+dojo.widget.tags.addParseTreeHandler("dojo:timepicker");
+
+dojo.requireAfterIf("html", "dojo.widget.html.TimePicker");
+
+dojo.widget.TimePicker.util = new function() {
+	// utility functions
+	this.toRfcDateTime = function(jsDate) {
+		if(!jsDate) {
+			jsDate = new Date();
+		}
+		return dojo.date.format(jsDate, "%Y-%m-%dT%H:%M:00%z");
+	}
+
+	this.fromRfcDateTime = function(rfcDate, useDefaultMinutes, isAnyTime) {
+		var tempDate = new Date();
+		if(!rfcDate || rfcDate.indexOf("T")==-1) {
+			if(useDefaultMinutes) {
+				tempDate.setMinutes(Math.floor(tempDate.getMinutes()/5)*5);
+			} else {
+				tempDate.setMinutes(0);
+			}
+		} else {
+			var tempTime = rfcDate.split("T")[1].split(":");
+			// fullYear, month, date
+			var tempDate = new Date();
+			tempDate.setHours(tempTime[0]);
+			tempDate.setMinutes(tempTime[1]);
+		}
+		return tempDate;
+	}
+
+	this.toAmPmHour = function(hour) {
+		var amPmHour = hour;
+		var isAm = true;
+		if (amPmHour == 0) {
+			amPmHour = 12;
+		} else if (amPmHour>12) {
+			amPmHour = amPmHour - 12;
+			isAm = false;
+		} else if (amPmHour == 12) {
+			isAm = false;
+		}
+		return [amPmHour, isAm];
+	}
+
+	this.fromAmPmHour = function(amPmHour, isAm) {
+		var hour = parseInt(amPmHour, 10);
+		if(isAm && hour == 12) {
+			hour = 0;
+		} else if (!isAm && hour<12) {
+			hour = hour + 12;
+		}
+		return hour;
+	}
+}


Property changes on: trunk/PhotoOp/root/static/js/src/widget/TimePicker.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/TitlePane.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/TitlePane.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/TitlePane.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -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.provide("dojo.widget.TitlePane");
+dojo.requireAfterIf("html", "dojo.widget.html.TitlePane");


Property changes on: trunk/PhotoOp/root/static/js/src/widget/TitlePane.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/Toggler.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/Toggler.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/Toggler.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,40 @@
+/*
+	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.Toggler");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.event.*");
+
+// clicking on this node shows/hides another widget
+
+dojo.widget.Toggler = function(){
+	dojo.widget.DomWidget.call(this);
+}
+
+dojo.inherits(dojo.widget.Toggler, dojo.widget.DomWidget);
+
+dojo.lang.extend(dojo.widget.Toggler, {
+	widgetType: "Toggler",
+	
+	// Associated widget 
+	targetId: '',
+	
+	fillInTemplate: function() {
+		dojo.event.connect(this.domNode, "onclick", this, "onClick");
+	},
+	
+	onClick: function() {
+		var pane = dojo.widget.byId(this.targetId);
+		if(!pane){ return; }
+		pane.explodeSrc = this.domNode;
+		pane.toggleShowing();
+	}
+});
+dojo.widget.tags.addParseTreeHandler("dojo:toggler");


Property changes on: trunk/PhotoOp/root/static/js/src/widget/Toggler.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/Toolbar.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/Toolbar.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/Toolbar.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,935 @@
+/*
+	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.ToolbarContainer");
+dojo.provide("dojo.widget.html.ToolbarContainer");
+dojo.provide("dojo.widget.Toolbar");
+dojo.provide("dojo.widget.html.Toolbar");
+dojo.provide("dojo.widget.ToolbarItem");
+dojo.provide("dojo.widget.html.ToolbarButtonGroup");
+dojo.provide("dojo.widget.html.ToolbarButton");
+dojo.provide("dojo.widget.html.ToolbarDialog");
+dojo.provide("dojo.widget.html.ToolbarMenu");
+dojo.provide("dojo.widget.html.ToolbarSeparator");
+dojo.provide("dojo.widget.html.ToolbarSpace");
+dojo.provide("dojo.widget.Icon");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.html");
+
+/* ToolbarContainer
+ *******************/
+dojo.widget.tags.addParseTreeHandler("dojo:toolbarContainer");
+dojo.widget.html.ToolbarContainer = function() {
+	dojo.widget.HtmlWidget.call(this);
+}
+dojo.inherits(dojo.widget.html.ToolbarContainer, dojo.widget.HtmlWidget);
+dojo.lang.extend(dojo.widget.html.ToolbarContainer, {
+	widgetType: "ToolbarContainer",
+	isContainer: true,
+
+	templateString: '<div class="toolbarContainer" dojoAttachPoint="containerNode"></div>',
+	templateCssPath: dojo.uri.dojoUri("src/widget/templates/HtmlToolbar.css"),
+
+	getItem: function(name) {
+		if(name instanceof dojo.widget.ToolbarItem) { return name; }
+		for(var i = 0; i < this.children.length; i++) {
+			var child = this.children[i];
+			if(child instanceof dojo.widget.html.Toolbar) {
+				var item = child.getItem(name);
+				if(item) { return item; }
+			}
+		}
+		return null;
+	},
+
+	getItems: function() {
+		var items = [];
+		for(var i = 0; i < this.children.length; i++) {
+			var child = this.children[i];
+			if(child instanceof dojo.widget.html.Toolbar) {
+				items = items.concat(child.getItems());
+			}
+		}
+		return items;
+	},
+
+	enable: function() {
+		for(var i = 0; i < this.children.length; i++) {
+			var child = this.children[i];
+			if(child instanceof dojo.widget.html.Toolbar) {
+				child.enable.apply(child, arguments);
+			}
+		}
+	},
+
+	disable: function() {
+		for(var i = 0; i < this.children.length; i++) {
+			var child = this.children[i];
+			if(child instanceof dojo.widget.html.Toolbar) {
+				child.disable.apply(child, arguments);
+			}
+		}
+	},
+
+	select: function(name) {
+		for(var i = 0; i < this.children.length; i++) {
+			var child = this.children[i];
+			if(child instanceof dojo.widget.html.Toolbar) {
+				child.select(arguments);
+			}
+		}
+	},
+
+	deselect: function(name) {
+		for(var i = 0; i < this.children.length; i++) {
+			var child = this.children[i];
+			if(child instanceof dojo.widget.html.Toolbar) {
+				child.deselect(arguments);
+			}
+		}
+	},
+
+	getItemsState: function() {
+		var values = {};
+		for(var i = 0; i < this.children.length; i++) {
+			var child = this.children[i];
+			if(child instanceof dojo.widget.html.Toolbar) {
+				dojo.lang.mixin(values, child.getItemsState());
+			}
+		}
+		return values;
+	},
+
+	getItemsActiveState: function() {
+		var values = {};
+		for(var i = 0; i < this.children.length; i++) {
+			var child = this.children[i];
+			if(child instanceof dojo.widget.html.Toolbar) {
+				dojo.lang.mixin(values, child.getItemsActiveState());
+			}
+		}
+		return values;
+	},
+
+	getItemsSelectedState: function() {
+		var values = {};
+		for(var i = 0; i < this.children.length; i++) {
+			var child = this.children[i];
+			if(child instanceof dojo.widget.html.Toolbar) {
+				dojo.lang.mixin(values, child.getItemsSelectedState());
+			}
+		}
+		return values;
+	}
+});
+
+/* Toolbar
+ **********/
+dojo.widget.tags.addParseTreeHandler("dojo:toolbar");
+dojo.widget.html.Toolbar = function() {
+	dojo.widget.HtmlWidget.call(this);
+}
+dojo.inherits(dojo.widget.html.Toolbar, dojo.widget.HtmlWidget);
+dojo.lang.extend(dojo.widget.html.Toolbar, {
+	widgetType: "Toolbar",
+	isContainer: true,
+
+	templateString: '<div class="toolbar" dojoAttachPoint="containerNode" unselectable="on" dojoOnMouseover="_onmouseover" dojoOnMouseout="_onmouseout" dojoOnClick="_onclick" dojoOnMousedown="_onmousedown" dojoOnMouseup="_onmouseup"></div>',
+	//templateString: '<div class="toolbar" dojoAttachPoint="containerNode" unselectable="on"></div>',
+
+	// given a node, tries to find it's toolbar item
+	_getItem: function(node) {
+		var start = new Date();
+		var widget = null;
+		while(node && node != this.domNode) {
+			if(dojo.html.hasClass(node, "toolbarItem")) {
+				var widgets = dojo.widget.manager.getWidgetsByFilter(function(w) { return w.domNode == node; });
+				if(widgets.length == 1) {
+					widget = widgets[0];
+					break;
+				} else if(widgets.length > 1) {
+					dojo.raise("Toolbar._getItem: More than one widget matches the node");
+				}
+			}
+			node = node.parentNode;
+		}
+		return widget;
+	},
+
+	_onmouseover: function(e) {
+		var widget = this._getItem(e.target);
+		if(widget && widget._onmouseover) { widget._onmouseover(e); }
+	},
+
+	_onmouseout: function(e) {
+		var widget = this._getItem(e.target);
+		if(widget && widget._onmouseout) { widget._onmouseout(e); }
+	},
+
+	_onclick: function(e) {
+		var widget = this._getItem(e.target);
+		if(widget && widget._onclick){ 
+			widget._onclick(e);
+		}
+	},
+
+	_onmousedown: function(e) {
+		var widget = this._getItem(e.target);
+		if(widget && widget._onmousedown) { widget._onmousedown(e); }
+	},
+
+	_onmouseup: function(e) {
+		var widget = this._getItem(e.target);
+		if(widget && widget._onmouseup) { widget._onmouseup(e); }
+	},
+
+	addChild: function(item, pos, props) {
+		var widget = dojo.widget.ToolbarItem.make(item, null, props);
+		var ret = dojo.widget.html.Toolbar.superclass.addChild.call(this, widget, null, pos, null);
+		return ret;
+	},
+
+	push: function() {
+		for(var i = 0; i < arguments.length; i++) {
+			this.addChild(arguments[i]);
+		}
+	},
+
+	getItem: function(name) {
+		if(name instanceof dojo.widget.ToolbarItem) { return name; }
+		for(var i = 0; i < this.children.length; i++) {
+			var child = this.children[i];
+			if(child instanceof dojo.widget.ToolbarItem
+				&& child._name == name) { return child; }
+		}
+		return null;
+	},
+
+	getItems: function() {
+		var items = [];
+		for(var i = 0; i < this.children.length; i++) {
+			var child = this.children[i];
+			if(child instanceof dojo.widget.ToolbarItem) {
+				items.push(child);
+			}
+		}
+		return items;
+	},
+
+	getItemsState: function() {
+		var values = {};
+		for(var i = 0; i < this.children.length; i++) {
+			var child = this.children[i];
+			if(child instanceof dojo.widget.ToolbarItem) {
+				values[child._name] = {
+					selected: child._selected,
+					enabled: child._enabled
+				};
+			}
+		}
+		return values;
+	},
+
+	getItemsActiveState: function() {
+		var values = this.getItemsState();
+		for(var item in values) {
+			values[item] = values[item].enabled;
+		}
+		return values;
+	},
+
+	getItemsSelectedState: function() {
+		var values = this.getItemsState();
+		for(var item in values) {
+			values[item] = values[item].selected;
+		}
+		return values;
+	},
+
+	enable: function() {
+		var items = arguments.length ? arguments : this.children;
+		for(var i = 0; i < items.length; i++) {
+			var child = this.getItem(items[i]);
+			if(child instanceof dojo.widget.ToolbarItem) {
+				child.enable(false, true);
+			}
+		}
+	},
+
+	disable: function() {
+		var items = arguments.length ? arguments : this.children;
+		for(var i = 0; i < items.length; i++) {
+			var child = this.getItem(items[i]);
+			if(child instanceof dojo.widget.ToolbarItem) {
+				child.disable();
+			}
+		}
+	},
+
+	select: function() {
+		for(var i = 0; i < arguments.length; i++) {
+			var name = arguments[i];
+			var item = this.getItem(name);
+			if(item) { item.select(); }
+		}
+	},
+
+	deselect: function() {
+		for(var i = 0; i < arguments.length; i++) {
+			var name = arguments[i];
+			var item = this.getItem(name);
+			if(item) { item.disable(); }
+		}
+	},
+
+	setValue: function() {
+		for(var i = 0; i < arguments.length; i += 2) {
+			var name = arguments[i], value = arguments[i+1];
+			var item = this.getItem(name);
+			if(item) {
+				if(item instanceof dojo.widget.ToolbarItem) {
+					item.setValue(value);
+				}
+			}
+		}
+	}
+});
+
+/* ToolbarItem hierarchy:
+	- ToolbarItem
+		- ToolbarButton
+		- ToolbarDialog
+			- ToolbarMenu
+		- ToolbarSeparator
+			- ToolbarSpace
+				- ToolbarFlexibleSpace
+*/
+
+
+/* ToolbarItem
+ **************/
+dojo.widget.ToolbarItem = function() {
+	dojo.widget.HtmlWidget.call(this);
+}
+dojo.inherits(dojo.widget.ToolbarItem, dojo.widget.HtmlWidget);
+dojo.lang.extend(dojo.widget.ToolbarItem, {
+	templateString: '<span unselectable="on" class="toolbarItem"></span>',
+
+	_name: null,
+	getName: function() { return this._name; },
+	setName: function(value) { return this._name = value; },
+	getValue: function() { return this.getName(); },
+	setValue: function(value) { return this.setName(value); },
+
+	_selected: false,
+	isSelected: function() { return this._selected; },
+	setSelected: function(is, force, preventEvent) {
+		if(!this._toggleItem && !force) { return; }
+		is = Boolean(is);
+		if(force || this._enabled && this._selected != is) {
+			this._selected = is;
+			this.update();
+			if(!preventEvent) {
+				this._fireEvent(is ? "onSelect" : "onDeselect");
+				this._fireEvent("onChangeSelect");
+			}
+		}
+	},
+	select: function(force, preventEvent) {
+		return this.setSelected(true, force, preventEvent);
+	},
+	deselect: function(force, preventEvent) {
+		return this.setSelected(false, force, preventEvent);
+	},
+
+	_toggleItem: false,
+	isToggleItem: function() { return this._toggleItem; },
+	setToggleItem: function(value) { this._toggleItem = Boolean(value); },
+
+	toggleSelected: function(force) {
+		return this.setSelected(!this._selected, force);
+	},
+
+	_enabled: true,
+	isEnabled: function() { return this._enabled; },
+	setEnabled: function(is, force, preventEvent) {
+		is = Boolean(is);
+		if(force || this._enabled != is) {
+			this._enabled = is;
+			this.update();
+			if(!preventEvent) {
+				this._fireEvent(this._enabled ? "onEnable" : "onDisable");
+				this._fireEvent("onChangeEnabled");
+			}
+		}
+		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);
+	},
+
+	_icon: null,
+	getIcon: function() { return this._icon; },
+	setIcon: function(value) {
+		var icon = dojo.widget.Icon.make(value);
+		if(this._icon) {
+			this._icon.setIcon(icon);
+		} else {
+			this._icon = icon;
+		}
+		var iconNode = this._icon.getNode();
+		if(iconNode.parentNode != this.domNode) {
+			if(this.domNode.hasChildNodes()) {
+				this.domNode.insertBefore(iconNode, this.domNode.firstChild);
+			} else {
+				this.domNode.appendChild(iconNode);
+			}
+		}
+		return this._icon;
+	},
+
+	// TODO: update the label node (this.labelNode?)
+	_label: "",
+	getLabel: function() { return this._label; },
+	setLabel: function(value) {
+		var ret = this._label = value;
+		if(!this.labelNode) {
+			this.labelNode = document.createElement("span");
+			this.domNode.appendChild(this.labelNode);
+		}
+		this.labelNode.innerHTML = "";
+		this.labelNode.appendChild(document.createTextNode(this._label));
+		this.update();
+		return ret;
+	},
+
+	// fired from: setSelected, setEnabled, setLabel
+	update: function() {
+		if(this._enabled) {
+			dojo.html.removeClass(this.domNode, "disabled");
+			if(this._selected) {
+				dojo.html.addClass(this.domNode, "selected");
+			} else {
+				dojo.html.removeClass(this.domNode, "selected");
+			}
+		} else {
+			this._selected = false;
+			dojo.html.addClass(this.domNode, "disabled");
+			dojo.html.removeClass(this.domNode, "down");
+			dojo.html.removeClass(this.domNode, "hover");
+		}
+		this._updateIcon();
+	},
+
+	_updateIcon: function() {
+		if(this._icon) {
+			if(this._enabled) {
+				if(this._cssHover) {
+					this._icon.hover();
+				} else if(this._selected) {
+					this._icon.select();
+				} else {
+					this._icon.enable();
+				}
+			} else {
+				this._icon.disable();
+			}
+		}
+	},
+
+	_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);
+		}
+	},
+
+	_onmouseover: function(e) {
+		if(!this._enabled) { return };
+		dojo.html.addClass(this.domNode, "hover");
+	},
+
+	_onmouseout: function(e) {
+		dojo.html.removeClass(this.domNode, "hover");
+		dojo.html.removeClass(this.domNode, "down");
+		if(!this._selected) {
+			dojo.html.removeClass(this.domNode, "selected");
+		}
+	},
+
+	_onclick: function(e) {
+		// FIXME: buttons never seem to have this._enabled set to true on Opera 9
+		// dojo.debug("widget:", this.widgetType, ":", this.getName(), ", enabled:", this._enabled);
+		if(this._enabled && !this._toggleItem) {
+			this._fireEvent("onClick");
+		}
+	},
+
+	_onmousedown: function(e) {
+		if(e.preventDefault) { e.preventDefault(); }
+		if(!this._enabled) { return };
+		dojo.html.addClass(this.domNode, "down");
+		if(this._toggleItem) {
+			if(this.parent.preventDeselect && this._selected) {
+				return;
+			}
+			this.toggleSelected();
+		}
+	},
+
+	_onmouseup: function(e) {
+		dojo.html.removeClass(this.domNode, "down");
+	},
+
+	fillInTemplate: function(args, frag) {
+		if(args.name) { this._name = args.name; }
+		if(args.selected) { this.select(); }
+		if(args.disabled) { this.disable(); }
+		if(args.label) { this.setLabel(args.label); }
+		if(args.icon) { this.setIcon(args.icon); }
+		if(args.toggleitem||args.toggleItem) { this.setToggleItem(true); }
+	}
+});
+
+dojo.widget.ToolbarItem.make = function(wh, whIsType, props) {
+	var item = null;
+
+	if(wh instanceof Array) {
+		item = dojo.widget.createWidget("ToolbarButtonGroup", props);
+		item.setName(wh[0]);
+		for(var i = 1; i < wh.length; i++) {
+			item.addChild(wh[i]);
+		}
+	} else if(wh instanceof dojo.widget.ToolbarItem) {
+		item = wh;
+	} else if(wh instanceof dojo.uri.Uri) {
+		item = dojo.widget.createWidget("ToolbarButton",
+			dojo.lang.mixin(props||{}, {icon: new dojo.widget.Icon(wh.toString())}));
+	} else if(whIsType) {
+		item = dojo.widget.createWidget(wh, props)
+	} else if(typeof wh == "string" || wh instanceof String) {
+		switch(wh.charAt(0)) {
+			case "|":
+			case "-":
+			case "/":
+				item = dojo.widget.createWidget("ToolbarSeparator", props);
+				break;
+			case " ":
+				if(wh.length == 1) {
+					item = dojo.widget.createWidget("ToolbarSpace", props);
+				} else {
+					item = dojo.widget.createWidget("ToolbarFlexibleSpace", props);
+				}
+				break;
+			default:
+				if(/\.(gif|jpg|jpeg|png)$/i.test(wh)) {
+					item = dojo.widget.createWidget("ToolbarButton",
+						dojo.lang.mixin(props||{}, {icon: new dojo.widget.Icon(wh.toString())}));
+				} else {
+					item = dojo.widget.createWidget("ToolbarButton",
+						dojo.lang.mixin(props||{}, {label: wh.toString()}));
+				}
+		}
+	} else if(wh && wh.tagName && /^img$/i.test(wh.tagName)) {
+		item = dojo.widget.createWidget("ToolbarButton",
+			dojo.lang.mixin(props||{}, {icon: wh}));
+	} else {
+		item = dojo.widget.createWidget("ToolbarButton",
+			dojo.lang.mixin(props||{}, {label: wh.toString()}));
+	}
+	return item;
+}
+
+/* ToolbarButtonGroup
+ *********************/
+dojo.widget.tags.addParseTreeHandler("dojo:toolbarButtonGroup");
+dojo.widget.html.ToolbarButtonGroup = function() {
+	dojo.widget.ToolbarItem.call(this);
+}
+dojo.inherits(dojo.widget.html.ToolbarButtonGroup, dojo.widget.ToolbarItem);
+dojo.lang.extend(dojo.widget.html.ToolbarButtonGroup, {
+	widgetType: "ToolbarButtonGroup",
+	isContainer: true,
+
+	templateString: '<span unselectable="on" class="toolbarButtonGroup" dojoAttachPoint="containerNode"></span>',
+
+	// if a button has the same name, it will be selected
+	// if this is set to a number, the button at that index will be selected
+	defaultButton: "",
+
+    postCreate: function() {
+        for (var i = 0; i < this.children.length; i++) {
+            this._injectChild(this.children[i]);
+        }
+    },
+
+	addChild: function(item, pos, props) {
+		var widget = dojo.widget.ToolbarItem.make(item, null, dojo.lang.mixin(props||{}, {toggleItem:true}));
+		var ret = dojo.widget.html.ToolbarButtonGroup.superclass.addChild.call(this, widget, null, pos, null);
+        this._injectChild(widget);
+        return ret;
+    },
+
+    _injectChild: function(widget) {
+        dojo.event.connect(widget, "onSelect", this, "onChildSelected");
+        dojo.event.connect(widget, "onDeselect", this, "onChildDeSelected");
+        if(widget._name == this.defaultButton
+			|| (typeof this.defaultButton == "number"
+			&& this.children.length-1 == this.defaultButton)) {
+			widget.select(false, true);
+		}
+	},
+
+	getItem: function(name) {
+		if(name instanceof dojo.widget.ToolbarItem) { return name; }
+		for(var i = 0; i < this.children.length; i++) {
+			var child = this.children[i];
+			if(child instanceof dojo.widget.ToolbarItem
+				&& child._name == name) { return child; }
+		}
+		return null;
+	},
+
+	getItems: function() {
+		var items = [];
+		for(var i = 0; i < this.children.length; i++) {
+			var child = this.children[i];
+			if(child instanceof dojo.widget.ToolbarItem) {
+				items.push(child);
+			}
+		}
+		return items;
+	},
+
+	onChildSelected: function(e) {
+		this.select(e._name);
+	},
+
+	onChildDeSelected: function(e) {
+		this._fireEvent("onChangeSelect", this._value);
+	},
+
+	enable: function(force, preventEvent) {
+		for(var i = 0; i < this.children.length; i++) {
+			var child = this.children[i];
+			if(child instanceof dojo.widget.ToolbarItem) {
+				child.enable(force, preventEvent);
+				if(child._name == this._value) {
+					child.select(force, preventEvent);
+				}
+			}
+		}
+	},
+
+	disable: function(force, preventEvent) {
+		for(var i = 0; i < this.children.length; i++) {
+			var child = this.children[i];
+			if(child instanceof dojo.widget.ToolbarItem) {
+				child.disable(force, preventEvent);
+			}
+		}
+	},
+
+	_value: "",
+	getValue: function() { return this._value; },
+
+	select: function(name, force, preventEvent) {
+		for(var i = 0; i < this.children.length; i++) {
+			var child = this.children[i];
+			if(child instanceof dojo.widget.ToolbarItem) {
+				if(child._name == name) {
+					child.select(force, preventEvent);
+					this._value = name;
+				} else {
+					child.deselect(true, true);
+				}
+			}
+		}
+		if(!preventEvent) {
+			this._fireEvent("onSelect", this._value);
+			this._fireEvent("onChangeSelect", this._value);
+		}
+	},
+	setValue: this.select,
+
+	preventDeselect: false // if true, once you select one, you can't have none selected
+});
+
+/* ToolbarButton
+ ***********************/
+dojo.widget.tags.addParseTreeHandler("dojo:toolbarButton");
+dojo.widget.html.ToolbarButton = function() {
+	dojo.widget.ToolbarItem.call(this);
+}
+dojo.inherits(dojo.widget.html.ToolbarButton, dojo.widget.ToolbarItem);
+dojo.lang.extend(dojo.widget.html.ToolbarButton, {
+	widgetType: "ToolbarButton",
+
+	fillInTemplate: function(args, frag) {
+		dojo.widget.html.ToolbarButton.superclass.fillInTemplate.call(this, args, frag);
+		dojo.html.addClass(this.domNode, "toolbarButton");
+		if(this._icon) {
+			this.setIcon(this._icon);
+		}
+		if(this._label) {
+			this.setLabel(this._label);
+		}
+
+		if(!this._name) {
+			if(this._label) {
+				this.setName(this._label);
+			} else if(this._icon) {
+				var src = this._icon.getSrc("enabled").match(/[\/^]([^\.\/]+)\.(gif|jpg|jpeg|png)$/i);
+				if(src) { this.setName(src[1]); }
+			} else {
+				this._name = this._widgetId;
+			}
+		}
+	}
+});
+
+/* ToolbarDialog
+ **********************/
+dojo.widget.tags.addParseTreeHandler("dojo:toolbarDialog");
+dojo.widget.html.ToolbarDialog = function() {
+	dojo.widget.html.ToolbarButton.call(this);
+}
+dojo.inherits(dojo.widget.html.ToolbarDialog, dojo.widget.html.ToolbarButton);
+dojo.lang.extend(dojo.widget.html.ToolbarDialog, {
+	widgetType: "ToolbarDialog",
+	
+	fillInTemplate: function (args, frag) {
+		dojo.widget.html.ToolbarDialog.superclass.fillInTemplate.call(this, args, frag);
+		dojo.event.connect(this, "onSelect", this, "showDialog");
+		dojo.event.connect(this, "onDeselect", this, "hideDialog");
+	},
+	
+	showDialog: function (e) {
+		dojo.lang.setTimeout(dojo.event.connect, 1, document, "onmousedown", this, "deselect");
+	},
+	
+	hideDialog: function (e) {
+		dojo.event.disconnect(document, "onmousedown", this, "deselect");
+	}
+
+});
+
+/* ToolbarMenu
+ **********************/
+dojo.widget.tags.addParseTreeHandler("dojo:toolbarMenu");
+dojo.widget.html.ToolbarMenu = function() {
+	dojo.widget.html.ToolbarDialog.call(this);
+
+	this.widgetType = "ToolbarMenu";
+}
+dojo.inherits(dojo.widget.html.ToolbarMenu, dojo.widget.html.ToolbarDialog);
+
+/* ToolbarMenuItem
+ ******************/
+dojo.widget.ToolbarMenuItem = function() {
+}
+
+/* ToolbarSeparator
+ **********************/
+dojo.widget.tags.addParseTreeHandler("dojo:toolbarSeparator");
+dojo.widget.html.ToolbarSeparator = function() {
+    dojo.widget.ToolbarItem.call(this);
+}
+dojo.inherits(dojo.widget.html.ToolbarSeparator, dojo.widget.ToolbarItem);
+dojo.lang.extend(dojo.widget.html.ToolbarSeparator, {
+	widgetType: "ToolbarSeparator",
+	templateString: '<span unselectable="on" class="toolbarItem toolbarSeparator"></span>',
+
+	defaultIconPath: new dojo.uri.dojoUri("src/widget/templates/buttons/-.gif"),
+
+	fillInTemplate: function(args, frag, skip) {
+		dojo.widget.html.ToolbarSeparator.superclass.fillInTemplate.call(this, args, frag);
+		this._name = this.widgetId;
+		if(!skip) {
+			if(!this._icon) {
+				this.setIcon(this.defaultIconPath);
+			}
+			this.domNode.appendChild(this._icon.getNode());
+		}
+	},
+
+	// don't want events!
+	_onmouseover: null, 
+    _onmouseout: null, 
+    _onclick: null, 
+    _onmousedown: null, 
+    _onmouseup: null 
+});
+
+/* ToolbarSpace
+ **********************/
+dojo.widget.tags.addParseTreeHandler("dojo:toolbarSpace");
+dojo.widget.html.ToolbarSpace = function() {
+	dojo.widget.html.ToolbarSeparator.call(this);
+}
+dojo.inherits(dojo.widget.html.ToolbarSpace, dojo.widget.html.ToolbarSeparator);
+dojo.lang.extend(dojo.widget.html.ToolbarSpace, {
+    widgetType: "ToolbarSpace",
+
+	fillInTemplate: function(args, frag, skip) {
+		dojo.widget.html.ToolbarSpace.superclass.fillInTemplate.call(this, args, frag, true);
+		if(!skip) {
+			dojo.html.addClass(this.domNode, "toolbarSpace");
+		}
+	}
+});
+
+/* ToolbarSelect
+ ******************/ 
+
+dojo.widget.tags.addParseTreeHandler("dojo:toolbarSelect");
+dojo.widget.html.ToolbarSelect = function() {
+	dojo.widget.ToolbarItem.call(this);
+}
+dojo.inherits(dojo.widget.html.ToolbarSelect, dojo.widget.ToolbarItem);
+dojo.lang.extend(dojo.widget.html.ToolbarSelect, {
+    widgetType: "ToolbarSelect",
+	templateString: '<span class="toolbarItem toolbarSelect" unselectable="on"><select dojoAttachPoint="selectBox" dojoOnChange="changed"></select></span>',
+
+	fillInTemplate: function(args, frag) {
+		dojo.widget.html.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.html.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);
+}


Property changes on: trunk/PhotoOp/root/static/js/src/widget/Toolbar.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/Tooltip.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/Tooltip.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/Tooltip.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,14 @@
+/*
+	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.Tooltip");
+dojo.require("dojo.widget.Widget");
+
+dojo.requireAfterIf("html", "dojo.widget.html.Tooltip");


Property changes on: trunk/PhotoOp/root/static/js/src/widget/Tooltip.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/Tree.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/Tree.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/Tree.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,569 @@
+/*
+	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
+*/
+
+/**
+ * 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.event.*");
+dojo.require("dojo.io.*");
+dojo.require("dojo.widget.HtmlWidget");
+dojo.require("dojo.widget.TreeNode");
+
+
+
+// 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: '<div class="dojoTree"></div>',
+
+	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<this.actionsDisabled.length; i++) {
+			this.actionsDisabled[i] = this.actionsDisabled[i].toUpperCase();
+		}
+
+		if (this.DNDMode == "off") {
+			this.DNDMode = 0;
+		} else if (this.DNDMode == "between") {
+			this.DNDMode = this.DNDModes.ONTO | this.DNDModes.BETWEEN;
+		} else if (this.DNDMode == "onto") {
+			this.DNDMode = this.DNDModes.ONTO;
+		}
+
+		this.expandLevel = parseInt(this.expandLevel);
+
+		this.initializeSelector();
+		this.initializeController();
+
+		if (this.menu) {
+			this.menu = dojo.widget.byId(this.menu);
+			this.menu.listenTree(this);
+		}
+
+
+		this.containerNode = this.domNode;
+
+	},
+
+
+	postCreate: function() {
+		this.createDOMNode();
+	},
+
+
+	createDOMNode: function() {
+
+		dojo.html.disableSelection(this.domNode);
+
+		for(var i=0; i<this.children.length; i++){
+			this.children[i].parent = this; // root nodes have tree as parent
+
+			var node = this.children[i].createDOMNode(this, 0);
+
+
+			this.domNode.appendChild(node);
+		}
+
+
+		if (!this.showRootGrid){
+			for(var i=0; i<this.children.length; i++){
+				this.children[i].expand();
+			}
+		}
+
+		dojo.event.topic.publish(this.eventNames.treeCreate, { source: this } );
+
+	},
+
+
+	destroy: function() {
+		dojo.event.topic.publish(this.tree.eventNames.treeDestroy, { source: this } );
+
+		return dojo.widget.HtmlWidget.prototype.destroy.apply(this, arguments);
+	},
+
+
+	addChild: function(child, index) {
+
+//		dojo.debug("doAddChild "+index+" called for "+child);
+
+		var message = {
+			child: child,
+			index: index,
+			parent: this,
+			// remember if dom was already initialized
+			// initialized => 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.dom.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; i<this.children.length; i++){
+			this.children[i].updateIconTree();
+		}
+
+	},
+
+	toString: function() {
+		return "["+this.widgetType+" ID:"+this.widgetId+"]"
+	},
+
+
+
+
+	/**
+	 * 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) {
+
+		//dojo.debug(child+" "+newParent+" at "+index);
+
+		var oldParent = child.parent;
+		var oldTree = child.tree;
+
+		this.doMove.apply(this, arguments);
+
+		var newParent = child.parent;
+		var newTree = child.tree;
+
+		var message = {
+				oldParent: oldParent, oldTree: oldTree,
+				newParent: newParent, newTree: newTree,
+				child: child
+		};
+
+		/* publish events here about structural changes for both source and target trees */
+		dojo.event.topic.publish(oldTree.eventNames.moveFrom, message);
+		dojo.event.topic.publish(newTree.eventNames.moveTo, message);
+
+	},
+
+
+	/* do actual parent change here. Write remove child first */
+	doMove: function(child, newParent, index) {
+		//var parent = child.parent;
+		child.parent.doRemoveNode(child);
+
+		newParent.doAddChild(child, index);
+	},
+
+
+
+// ================================ removeNode ===================================
+
+	removeNode: function(child) {
+		if (!child.parent) return;
+
+		var oldTree = child.tree;
+		var oldParent = child.parent;
+
+		var removedChild = this.doRemoveNode.apply(this, arguments);
+
+
+		dojo.event.topic.publish(this.tree.eventNames.removeNode,
+			{ child: removedChild, tree: oldTree, parent: oldParent }
+		);
+
+		return removedChild;
+	},
+
+
+	doRemoveNode: function(child) {
+		if (!child.parent) return;
+
+		var parent = child.parent;
+
+		var children = parent.children;
+
+
+		var index = child.getParentIndex();
+		if (index < 0) {
+			dojo.raise("Couldn't find node "+child+" for removal");
+		}
+
+
+		children.splice(index,1);
+		dojo.dom.removeNode(child.domNode);
+
+		if (parent.children.length == 0) {
+			parent.containerNode.style.display = "none";
+		}
+
+		// if WAS last node (children.length decreased already) and has prevSibling
+		if (index == children.length && index>0) {
+			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();
+	}
+});
+
+


Property changes on: trunk/PhotoOp/root/static/js/src/widget/Tree.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/TreeBasicController.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/TreeBasicController.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/TreeBasicController.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,294 @@
+/*
+	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.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<this.node.children.length; i++) {
+					var child = node.children[i];
+
+					_this.expandToLevel(child, this.expandLevel);
+				}
+			};
+		}
+
+		var h = new handler(node, level-1);
+
+
+		this.expand(node, false, h, h.process);
+
+	},
+
+
+
+
+	onTreeClick: function(message){
+		var node = message.source;
+
+		if(node.isLocked()) {
+			return false;
+		}
+
+		if (node.isExpanded){
+			this.collapse(node);
+		} else {
+			this.expand(node);
+		}
+	},
+
+	expand: function(node, sync, callObj, callFunc) {
+		node.expand();
+		if (callFunc) callFunc.apply(callObj, [node]);
+	},
+
+	collapse: function(node) {
+
+		node.collapse();
+	},
+
+// =============================== move ============================
+
+	/**
+	 * 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.actionIsDisabled(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.actionIsDisabled(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) {
+
+		/* move sourceTreeNode to new parent */
+		if (!this.canMove(child, newParent)) {
+			return false;
+		}
+
+		var result = this.doMove(child, newParent, index);
+
+		if (!result) return result;
+
+		if (newParent.isTreeNode) {
+			this.expand(newParent);
+		}
+
+		return result;
+	},
+
+	doMove: function(child, newParent, index) {
+		child.tree.move(child, newParent, index);
+
+		return true;
+	},
+
+// =============================== removeNode ============================
+
+
+	canRemoveNode: function(child) {
+		if (child.actionIsDisabled(child.actions.REMOVE)) {
+			return false;
+		}
+
+		return true;
+	},
+
+
+	removeNode: function(node, callObj, callFunc) {
+		if (!this.canRemoveNode(node)) {
+			return false;
+		}
+
+		return this.doRemoveNode(node, callObj, callFunc);
+	},
+
+
+	doRemoveNode: function(node, callObj, callFunc) {
+		node.tree.removeNode(node);
+
+		if (callFunc) {
+			callFunc.apply(dojo.lang.isUndefined(callObj) ? this : callObj, [node]);
+		}
+	},
+
+
+	// -----------------------------------------------------------------------------
+	//                             Create node stuff
+	// -----------------------------------------------------------------------------
+
+
+	canCreateChild: function(parent, index, data) {
+		if (parent.actionIsDisabled(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, callObj, callFunc) {
+		if (!this.canCreateChild(parent, index, data)) {
+			return false;
+		}
+
+		return this.doCreateChild.apply(this, arguments);
+	},
+
+	doCreateChild: function(parent, index, data, callObj, callFunc) {
+
+		var widgetType = data.widgetType ? data.widgetType : "TreeNode";
+
+		var newChild = dojo.widget.createWidget(widgetType, data);
+
+		parent.addChild(newChild, index);
+
+		this.expand(parent);
+
+		if (callFunc) {
+			callFunc.apply(callObj, [newChild]);
+		}
+
+		return newChild;
+	}
+
+
+
+});


Property changes on: trunk/PhotoOp/root/static/js/src/widget/TreeBasicController.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/TreeContextMenu.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/TreeContextMenu.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/TreeContextMenu.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,214 @@
+/*
+	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.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<nodes.length; i++) {
+			if (!nodes[i].isTreeNode) continue;
+			this.bindDomNode(nodes[i].labelNode);
+		}
+
+
+		/* bind context menu to all nodes that will be created in the future (e.g loaded from server)*/
+		var _this = this;
+		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.removeNode, this, "onRemoveNode");
+		dojo.event.topic.subscribe(tree.eventNames.addChild, this, "onAddChild");
+		dojo.event.topic.subscribe(tree.eventNames.treeDestroy, this, "onTreeDestroy");
+
+		this.listenedTrees.push(tree);
+
+	},
+
+	unlistenTree: function(tree) {
+		/* clear event listeners */
+
+		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.removeNode, this, "onRemoveNode");
+		dojo.event.topic.unsubscribe(tree.eventNames.addChild, this, "onAddChild");
+		dojo.event.topic.unsubscribe(tree.eventNames.treeDestroy, this, "onTreeDestroy");
+
+		for(var i=0; i<this.listenedTrees.length; i++){
+           if(this.listenedTrees[i] === tree){
+                   this.listenedTrees.splice(i, 1);
+                   break;
+           }
+		}
+	},
+
+	onTreeDestroy: function(message) {
+		this.unlistenTree(message.source);
+	},
+
+	bindTreeNode: function(node) {
+		var _this = this;
+		//dojo.debug("bind to "+node);
+		dojo.lang.forEach(node.getDescendants(),
+			function(e) {_this.bindDomNode(e.labelNode); }
+		);
+	},
+
+
+	unBindTreeNode: function(node) {
+		var _this = this;
+		//dojo.debug("Unbind from "+node);
+		dojo.lang.forEach(node.getDescendants(),
+			function(e) {_this.unBindDomNode(e.labelNode); }
+		);
+	},
+
+	onCreateDOMNode: function(message) {
+		this.bindTreeNode(message.source);
+	},
+
+
+	onMoveFrom: function(message) {
+		if (!dojo.lang.inArray(this.listenedTrees, message.newTree)) {
+			this.unBindTreeNode(message.child);
+		}
+	},
+
+	onMoveTo: function(message) {
+		if (dojo.lang.inArray(this.listenedTrees, message.newTree)) {
+			this.bindTreeNode(message.child);
+		}
+	},
+
+	onRemoveNode: function(message) {
+		this.unBindTreeNode(message.child);
+	},
+
+	onAddChild: function(message) {
+		if (message.domNodeInitialized) {
+			// dom node was there already => 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.treeActions.length; i++) {
+			this.treeActions[i] = this.treeActions[i].toUpperCase();
+		}
+
+	},
+
+	getTreeNode: function() {
+		var menu = this;
+
+		while (! (menu instanceof dojo.widget.TreeContextMenu) ) {
+			menu = menu.parent;
+		}
+
+		var source = menu.getTopOpenEvent().target;
+
+		while (!source.getAttribute('treeNode') && source.tagName != 'body') {
+			source = source.parentNode;
+		}
+		if (source.tagName == 'body') {
+			dojo.raise("treeNode not detected");
+		}
+		var treeNode = dojo.widget.manager.getWidgetById(source.getAttribute('treeNode'));
+
+		return treeNode;
+	},
+
+
+	menuOpen: function(message) {
+		var treeNode = this.getTreeNode();
+
+		this.setDisabled(false); // enable by default
+
+		var _this = this;
+		dojo.lang.forEach(_this.treeActions,
+			function(action) {
+				_this.setDisabled( treeNode.actionIsDisabled(action) );
+			}
+		);
+
+	},
+
+	toString: function() {
+		return "["+this.widgetType+" node "+this.getTreeNode()+"]";
+	}
+
+});
+
+


Property changes on: trunk/PhotoOp/root/static/js/src/widget/TreeContextMenu.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/TreeControllerExtension.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/TreeControllerExtension.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/TreeControllerExtension.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,95 @@
+/*
+	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
+*/
+
+/**
+ * Additional tree utils
+ *
+ */
+dojo.provide("dojo.widget.TreeControllerExtension");
+
+
+dojo.widget.TreeControllerExtension = function() { }
+
+dojo.lang.extend(dojo.widget.TreeControllerExtension, {
+
+	saveExpandedIndices: function(node, field) {
+		var obj = {};
+
+		for(var i=0; i<node.children.length; i++) {
+			if (node.children[i].isExpanded) {
+				var key = dojo.lang.isUndefined(field) ? i : node.children[i][field];
+				obj[key] = this.saveExpandedIndices(node.children[i], field);
+			}
+		}
+
+		return obj;
+	},
+
+
+	restoreExpandedIndices: function(node, savedIndices, field) {
+		var _this = this;
+
+		var handler = function(node, savedIndices) {
+			this.node = node; //.children[i];
+			this.savedIndices = savedIndices; //[i];
+			// recursively read next savedIndices level and apply to opened node
+			this.process = function() {
+				//dojo.debug("Callback applied for "+this.node);
+				_this.restoreExpandedIndices(this.node, this.savedIndices, field);
+			};
+		}
+
+
+		for(var i=0; i<node.children.length; i++) {
+			var child = node.children[i];
+
+			var found = false;
+			var key = -1;
+
+			//dojo.debug("Check "+child)
+			// process field set case
+			if (dojo.lang.isUndefined(field) && savedIndices[i]) {
+				found = true;
+				key = i;
+			}
+
+			// process case when field is not set
+			if (field) {
+				for(var key in savedIndices) {
+					//dojo.debug("Compare "+key+" "+child[field])
+					if (key == child[field]) {
+						found = true;
+						break;
+					}
+				}
+			}
+
+			// if we found anything - expand it
+			if (found) {
+				//dojo.debug("Found at "+key)
+				var h = new handler(child, savedIndices[key]);
+				_this.expand(child, false, h, h.process);
+			} else if (child.isExpanded) { // not found, so collapse
+				//dojo.debug("Collapsing all descendants "+node.children[i])
+				dojo.lang.forEach(child.getDescendants(), function(elem) { _this.collapse(elem); });
+				//this.collapse(node.children[i]);
+			}
+
+		}
+
+
+	}
+
+});
+
+
+
+
+


Property changes on: trunk/PhotoOp/root/static/js/src/widget/TreeControllerExtension.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/TreeLoadingController.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/TreeLoadingController.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/TreeLoadingController.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,217 @@
+/*
+	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.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"));
+	},
+
+
+
+	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<newChildren.length; i++) {
+			// looks like dojo.widget.manager needs no special "add" command
+			newChildren[i] = dojo.widget.createWidget(node.widgetType, newChildren[i]);
+			node.addChild(newChildren[i]);
+		}
+
+
+		//node.addAllChildren(newChildren);
+
+		node.state = node.loadStates.LOADED;
+
+		//dojo.debug(callFunc);
+
+		if (dojo.lang.isFunction(callFunc)) {
+			callFunc.apply(dojo.lang.isUndefined(callObj) ? this : callObj, [node, newChildren]);
+		}
+		//this.expand(node);
+	},
+
+	getInfo: function(obj) {
+		return obj.getInfo();
+	},
+
+	runRPC: function(kw) {
+		var _this = this;
+
+		var handle = function(type, data, evt) {
+			// unlock BEFORE any processing is done
+			// so errorHandler may apply locking
+			if (kw.lock) {
+				dojo.lang.forEach(kw.lock,
+					function(t) { t.unlock() }
+				);
+			}
+
+			if(type == "load"){
+				kw.load.call(this, data);
+			}else{
+				this.RPCErrorHandler(type, data, evt);
+			}
+
+		}
+
+		if (kw.lock) {
+			dojo.lang.forEach(kw.lock,
+				function(t) { t.lock() }
+			);
+		}
+
+
+		dojo.io.bind({
+			url: kw.url,
+			/* I hitch to get this.loadOkHandler */
+			handle: dojo.lang.hitch(this, handle),
+			mimetype: "text/json",
+			preventCache: true,
+			sync: kw.sync,
+			content: { data: dojo.json.serialize(kw.params) }
+		});
+	},
+
+
+
+	/**
+	 * Load children of the node from server
+	 * Synchroneous loading doesn't break control flow
+	 * I need sync mode for DnD
+	*/
+	loadRemote: function(node, sync, callObj, callFunc){
+		var _this = this;
+
+		var params = {
+			node: this.getInfo(node),
+			tree: this.getInfo(node.tree)
+		};
+
+		//dojo.debug(callFunc)
+
+		this.runRPC({
+			url: this.getRPCUrl('getChildren'),
+			load: function(result) {
+				_this.loadProcessResponse(node, result, callObj, callFunc) ;
+			},
+			sync: sync,
+			lock: [node],
+			params: params
+		});
+
+	},
+
+
+	expand: function(node, sync, callObj, callFunc) {
+
+		if (node.state == node.loadStates.UNCHECKED && node.isFolder) {
+
+			this.loadRemote(node, sync,
+				this,
+				function(node, newChildren) {
+					this.expand(node, sync, callObj, callFunc);
+				}
+			);
+
+			return;
+		}
+
+		dojo.widget.TreeBasicController.prototype.expand.apply(this, arguments);
+
+	},
+
+
+
+	doMove: function(child, newParent, index) {
+		/* load nodes into newParent in sync mode, if needed, first */
+		if (newParent.isTreeNode && newParent.state == newParent.loadStates.UNCHECKED) {
+			this.loadRemote(newParent, true);
+		}
+
+		return dojo.widget.TreeBasicController.prototype.doMove.apply(this, arguments);
+	},
+
+
+	doCreateChild: function(parent, index, data, callObj, callFunc) {
+
+		/* load nodes into newParent in sync mode, if needed, first */
+		if (parent.state == parent.loadStates.UNCHECKED) {
+			this.loadRemote(parent, true);
+		}
+
+		return dojo.widget.TreeBasicController.prototype.doCreateChild.apply(this, arguments);
+	}
+
+
+
+});


Property changes on: trunk/PhotoOp/root/static/js/src/widget/TreeLoadingController.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/TreeNode.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/TreeNode.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/TreeNode.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,534 @@
+/*
+	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.TreeNode");
+
+dojo.require("dojo.event.*");
+dojo.require("dojo.io.*");
+
+// make it a tag
+dojo.widget.tags.addParseTreeHandler("dojo:TreeNode");
+
+
+// # //////////
+
+dojo.widget.TreeNode = function() {
+	dojo.widget.HtmlWidget.call(this);
+
+	this.actionsDisabled = [];
+}
+
+dojo.inherits(dojo.widget.TreeNode, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.TreeNode, {
+	widgetType: "TreeNode",
+
+	loadStates: {
+		UNCHECKED: "UNCHECKED",
+    	LOADING: "LOADING",
+    	LOADED: "LOADED"
+	},
+
+
+	actions: {
+		MOVE: "MOVE",
+    	REMOVE: "REMOVE",
+    	EDIT: "EDIT",
+    	ADDCHILD: "ADDCHILD"
+	},
+
+	isContainer: true,
+
+	lockLevel: 0, // lock ++ unlock --, so nested locking works fine
+
+
+	templateString: ('<div class="dojoTreeNode"> '
+		+ '<span treeNode="${this.widgetId}" class="dojoTreeNodeLabel" dojoAttachPoint="labelNode"> '
+		+ '		<span dojoAttachPoint="titleNode" dojoAttachEvent="onClick: onTitleClick" class="dojoTreeNodeLabelTitle">${this.title}</span> '
+		+ '</span> '
+		+ '<span class="dojoTreeNodeAfterLabel" dojoAttachPoint="afterLabelNode">${this.afterLabel}</span> '
+		+ '<div dojoAttachPoint="containerNode" style="display:none"></div> '
+		+ '</div>').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<this.actionsDisabled.length; i++) {
+			this.actionsDisabled[i] = this.actionsDisabled[i].toUpperCase();
+		}
+
+		this.expandLevel = parseInt(this.expandLevel);
+
+	},
+
+
+	/**
+	 * Change visible node depth by appending/prepending with blankImgs
+	 * @param depthDiff Integer positive => move right, negative => move left
+	*/
+	adjustDepth: function(depthDiff) {
+
+		for(var i=0; i<this.children.length; i++) {
+			this.children[i].adjustDepth(depthDiff);
+		}
+
+		this.depth += depthDiff;
+
+		if (depthDiff>0) {
+			for(var i=0; i<depthDiff; i++) {
+				var img = this.tree.makeBlankImg();
+				this.imgs.unshift(img);
+				//dojo.debugShallow(this.domNode);
+				dojo.dom.insertBefore(this.imgs[0], this.domNode.firstChild);
+
+			}
+		}
+		if (depthDiff<0) {
+			for(var i=0; i<-depthDiff;i++) {
+				this.imgs.shift();
+				dojo.dom.removeNode(this.domNode.firstChild);
+			}
+		}
+
+	},
+
+
+	markLoading: function() {
+		this._markLoadingSavedIcon = this.expandIcon.src;
+		this.expandIcon.src = this.tree.expandIconSrcLoading;
+	},
+
+	// if icon is "Loading" then
+	unMarkLoading: function() {
+		if (!this._markLoadingSavedIcon) return;
+
+		var im = new Image();
+		im.src = this.tree.expandIconSrcLoading;
+
+		//dojo.debug("Unmark "+this.expandIcon.src+" : "+im.src);
+		if (this.expandIcon.src == im.src) {
+			this.expandIcon.src = this._markLoadingSavedIcon;
+		}
+		this._markLoadingSavedIcon = null;
+	},
+
+
+	setFolder: function() {
+		dojo.event.connect(this.expandIcon, 'onclick', this, 'onTreeClick');
+		this.expandIcon.src = this.isExpanded ? this.tree.expandIconSrcMinus : this.tree.expandIconSrcPlus;
+		this.isFolder = true;
+	},
+
+
+	createDOMNode: function(tree, depth){
+
+		this.tree = tree;
+		this.depth = depth;
+
+
+		//
+		// add the tree icons
+		//
+
+		this.imgs = [];
+
+		for(var i=0; i<this.depth+1; i++){
+
+			var img = this.tree.makeBlankImg();
+
+			this.domNode.insertBefore(img, this.labelNode);
+
+			this.imgs.push(img);
+		}
+
+
+		this.expandIcon = this.imgs[this.imgs.length-1];
+
+
+		this.childIcon = this.tree.makeBlankImg();
+
+		// add to images before the title
+		this.imgs.push(this.childIcon);
+
+		dojo.dom.insertBefore(this.childIcon, this.titleNode);
+
+		// node with children(from source html) becomes folder on build stage.
+		if (this.children.length || this.isFolder) {
+			this.setFolder();
+		}
+		else {
+			// leaves are always loaded
+			//dojo.debug("Set "+this+" state to loaded");
+			this.state = this.loadStates.LOADED;
+		}
+
+		dojo.event.connect(this.childIcon, 'onclick', this, 'onIconClick');
+
+
+		//
+		// create the child rows
+		//
+
+
+		for(var i=0; i<this.children.length; i++){
+			this.children[i].parent = this;
+
+			var node = this.children[i].createDOMNode(this.tree, this.depth+1);
+
+			this.containerNode.appendChild(node);
+		}
+
+
+		if (this.children.length) {
+			this.state = this.loadStates.LOADED;
+		}
+
+		this.updateIcons();
+
+		this.domNodeInitialized = true;
+
+		dojo.event.topic.publish(this.tree.eventNames.createDOMNode, { source: this } );
+
+		return this.domNode;
+	},
+
+	onTreeClick: function(e){
+		dojo.event.topic.publish(this.tree.eventNames.treeClick, { source: this, event: e });
+	},
+
+	onIconClick: function(e){
+		dojo.event.topic.publish(this.tree.eventNames.iconClick, { source: this, event: e });
+	},
+
+	onTitleClick: function(e){
+		dojo.event.topic.publish(this.tree.eventNames.titleClick, { source: this, event: e });
+	},
+
+	markSelected: function() {
+		dojo.html.addClass(this.titleNode, 'dojoTreeNodeLabelSelected');
+	},
+
+
+	unMarkSelected: function() {
+		//dojo.debug('unmark')
+		dojo.html.removeClass(this.titleNode, 'dojoTreeNodeLabelSelected');
+	},
+
+	updateExpandIcon: function() {
+		if (this.isFolder){
+			this.expandIcon.src = this.isExpanded ? this.tree.expandIconSrcMinus : this.tree.expandIconSrcPlus;
+		} else {
+			this.expandIcon.src = this.tree.blankIconSrc;
+		}
+	},
+
+	/* set the grid under the expand icon */
+	updateExpandGrid: function() {
+
+		if (this.tree.showGrid){
+			if (this.depth){
+				this.setGridImage(-2, this.isLastNode() ? this.tree.gridIconSrcL : this.tree.gridIconSrcT);
+			}else{
+				if (this.isFirstNode()){
+					this.setGridImage(-2, this.isLastNode() ? this.tree.gridIconSrcX : this.tree.gridIconSrcY);
+				}else{
+					this.setGridImage(-2, this.isLastNode() ? this.tree.gridIconSrcL : this.tree.gridIconSrcT);
+				}
+			}
+		}else{
+			this.setGridImage(-2, this.tree.blankIconSrc);
+		}
+
+	},
+
+	/* set the grid under the child icon */
+	updateChildGrid: function() {
+
+		if ((this.depth || this.tree.showRootGrid) && this.tree.showGrid){
+			this.setGridImage(-1, (this.children.length && this.isExpanded) ? this.tree.gridIconSrcP : this.tree.gridIconSrcC);
+		}else{
+			if (this.tree.showGrid && !this.tree.showRootGrid){
+				this.setGridImage(-1, (this.children.length && this.isExpanded) ? this.tree.gridIconSrcZ : this.tree.blankIconSrc);
+			}else{
+				this.setGridImage(-1, this.tree.blankIconSrc);
+			}
+		}
+
+
+	},
+
+	updateParentGrid: function() {
+		var parent = this.parent;
+
+		//dojo.debug("updateParentGrid "+this);
+
+		for(var i=0; i<this.depth; i++){
+
+			//dojo.debug("Parent "+parent);
+
+			var idx = this.imgs.length-(3+i);
+			var img = (this.tree.showGrid && !parent.isLastNode()) ? this.tree.gridIconSrcV : this.tree.blankIconSrc;
+
+			//dojo.debug("Image "+img+" for "+idx);
+
+			this.setGridImage(idx, img);
+
+			parent = parent.parent;
+		}
+	},
+
+	updateExpandGridColumn: function() {
+		if (!this.tree.showGrid) return;
+
+		var _this = this;
+
+		var icon = this.isLastNode() ? this.tree.blankIconSrc : this.tree.gridIconSrcV;
+
+		dojo.lang.forEach(_this.getDescendants(),
+			function(node) { node.setGridImage(_this.depth, icon); }
+		);
+
+		this.updateExpandGrid();
+	},
+
+	updateIcons: function(){
+
+
+		//dojo.profile.start("updateIcons")
+
+		//dojo.debug("Update icons for "+this)
+		//dojo.debug(this.isFolder)
+
+		this.imgs[0].style.display = this.tree.showRootGrid ? 'inline' : 'none';
+
+
+		//
+		// set the expand icon
+		//
+
+
+		//
+		// set the child icon
+		//
+		this.buildChildIcon();
+
+		this.updateExpandGrid();
+		this.updateChildGrid();
+		this.updateParentGrid();
+
+
+
+		dojo.profile.stop("updateIcons")
+
+	},
+
+	buildChildIcon: function() {
+		// IE (others?) tries to download whatever is on src attribute so setting "url()" like before isnt a good idea
+		// Only results in a 404
+		if(this.childIconSrc){
+			this.childIcon.src = this.childIconSrc;
+		}
+		this.childIcon.style.display = this.childIconSrc ? 'inline' : 'none';
+	},
+
+	setGridImage: function(idx, src){
+
+		if (idx < 0){
+			idx = this.imgs.length + idx;
+		}
+
+		//if (idx >= 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+"]";
+
+	}
+
+});
+
+
+
+


Property changes on: trunk/PhotoOp/root/static/js/src/widget/TreeNode.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/TreeRPCController.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/TreeRPCController.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/TreeRPCController.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,171 @@
+/*
+	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.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);
+	}
+});


Property changes on: trunk/PhotoOp/root/static/js/src/widget/TreeRPCController.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/TreeSelector.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/TreeSelector.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/TreeSelector.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,183 @@
+/*
+	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.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<this.listenedTrees.length; i++){
+           if(this.listenedTrees[i] === tree){
+                   this.listenedTrees.splice(i, 1);
+                   break;
+           }
+		}
+	},
+
+
+	onTreeDestroy: function(message) {
+
+		this.unlistenTree(message.source);
+
+		if (this.dieWithTree) {
+			//dojo.debug("Killing myself "+this.widgetId);
+			this.destroy();
+			//dojo.debug("done");
+		}
+	},
+
+
+	// deselect node if parent is collapsed
+	onCollapse: function(message) {
+		if (!this.selectedNode) return;
+
+		var node = message.source;
+		var parent = this.selectedNode.parent;
+		while (parent !== node && parent.isTreeNode) {
+			parent = parent.parent;
+		}
+		if (parent.isTreeNode) {
+			this.deselect();
+		}
+	},
+
+
+
+	select: function(message) {
+		var node = message.source;
+		var e = message.event;
+
+		if (this.selectedNode === node) {
+			dojo.event.topic.publish(this.eventNames.dblselect, { node: node });
+			return;
+		}
+
+		if (this.selectedNode) {
+			this.deselect();
+		}
+
+		this.doSelect(node);
+
+		dojo.event.topic.publish(this.eventNames.select, {node: node} );
+	},
+
+	/**
+	 * Deselect node if target tree is out of our concern
+	 */
+	onMoveFrom: function(message) {
+		if (message.child !== this.selectedNode) {
+			return;
+		}
+
+		if (!dojo.lang.inArray(this.listenedTrees, message.newTree)) {
+			this.deselect();
+		}
+	},
+
+	onRemoveNode: function(message) {
+		if (message.child !== this.selectedNode) {
+			return;
+		}
+
+		this.deselect();
+	},
+
+	doSelect: function(node){
+
+		node.markSelected();
+
+		this.selectedNode = node;
+	},
+
+	deselect: function(){
+
+		var node = this.selectedNode;
+
+		this.selectedNode = null;
+		node.unMarkSelected();
+		dojo.event.topic.publish(this.eventNames.deselect, {node: node} );
+
+	}
+
+});
+
+
+


Property changes on: trunk/PhotoOp/root/static/js/src/widget/TreeSelector.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/Widget.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/Widget.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/Widget.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,576 @@
+/*
+	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.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, {
+	initializer: function() {								 
+		// 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
+
+	toString: function() {
+		return '[Widget ' + this.widgetType + ', ' + (this.widgetId || 'NO ID') + ']';
+	},
+
+	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<this.children.length; i++){
+			var child = this.children[i];
+			//dojo.debug(this.widgetId + " resizing child " + child.widgetId);
+			if( child.onResized ){
+				child.onResized();
+			}
+		}
+	},
+
+	create: function(args, fragment, parentComp){
+		// dojo.debug(this.widgetType, "create");
+		this.satisfyPropertySets(args, fragment, parentComp);
+		// dojo.debug(this.widgetType, "-> mixInProperties");
+		this.mixInProperties(args, fragment, parentComp);
+		// dojo.debug(this.widgetType, "-> postMixInProperties");
+		this.postMixInProperties(args, fragment, parentComp);
+		// dojo.debug(this.widgetType, "-> dojo.widget.manager.add");
+		dojo.widget.manager.add(this);
+		// dojo.debug(this.widgetType, "-> buildRendering");
+		this.buildRendering(args, fragment, parentComp);
+		// dojo.debug(this.widgetType, "-> initialize");
+		this.initialize(args, fragment, parentComp);
+		// dojo.debug(this.widgetType, "-> postInitialize");
+		this.postInitialize(args, fragment, parentComp);
+		// dojo.debug(this.widgetType, "-> postCreate");
+		this.postCreate(args, fragment, parentComp);
+		// dojo.debug(this.widgetType, "done!");
+		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; x<this.children.length; x++){
+			if(isFunc){
+				if(this.children[x] instanceof type){
+					ret.push(this.children[x]);
+				}
+			}else{
+				if(this.children[x].widgetType.toLowerCase() == type){
+					ret.push(this.children[x]);
+				}
+			}
+			if(recurse){
+				ret = ret.concat(this.children[x].getChildrenOfType(type, recurse));
+			}
+		}
+		return ret;
+	},
+
+	getDescendants: function(){
+		var result = [];
+		var stack = [this];
+		var elem;
+		while (elem = stack.pop()){
+			result.push(elem);
+			dojo.lang.forEach(elem.children, function(elem) { stack.push(elem); });
+		}
+		return result;
+	},
+
+	satisfyPropertySets: function(args){
+		// dojo.profile.start("satisfyPropertySets");
+		// get the default propsets for our component type
+		/*
+		var typePropSets = []; // FIXME: need to pull these from somewhere!
+		var localPropSets = []; // pull out propsets from the parser's return structure
+
+		// for(var x=0; x<args.length; x++){
+		// }
+
+		for(var x=0; x<typePropSets.length; x++){
+		}
+
+		for(var x=0; x<localPropSets.length; x++){
+		}
+		*/
+		// dojo.profile.end("satisfyPropertySets");
+		
+		return args;
+	},
+
+	mixInProperties: function(args, frag){
+		if((args["fastMixIn"])||(frag["fastMixIn"])){
+			// dojo.profile.start("mixInProperties_fastMixIn");
+			// fast mix in assumes case sensitivity, no type casting, etc...
+			// dojo.lang.mixin(this, args);
+			for(var x in args){
+				this[x] = args[x];
+			}
+			// dojo.profile.end("mixInProperties_fastMixIn");
+			return;
+		}
+		// dojo.profile.start("mixInProperties");
+		/*
+		 * the actual mix-in code attempts to do some type-assignment based on
+		 * PRE-EXISTING properties of the "this" object. When a named property
+		 * of a propset is located, it is first tested to make sure that the
+		 * current object already "has one". Properties which are undefined in
+		 * the base widget are NOT settable here. The next step is to try to
+		 * determine type of the pre-existing property. If it's a string, the
+		 * property value is simply assigned. If a function, the property is
+		 * replaced with a "new Function()" declaration. If an Array, the
+		 * system attempts to split the string value on ";" chars, and no
+		 * further processing is attempted (conversion of array elements to a
+		 * integers, for instance). If the property value is an Object
+		 * (testObj.constructor === Object), the property is split first on ";"
+		 * chars, secondly on ":" chars, and the resulting key/value pairs are
+		 * assigned to an object in a map style. The onus is on the property
+		 * user to ensure that all property values are converted to the
+		 * expected type before usage.
+		 */
+
+		var undef;
+
+		// NOTE: we cannot assume that the passed properties are case-correct
+		// (esp due to some browser bugs). Therefore, we attempt to locate
+		// properties for assignment regardless of case. This may cause
+		// problematic assignments and bugs in the future and will need to be
+		// documented with big bright neon lights.
+
+		// FIXME: fails miserably if a mixin property has a default value of null in 
+		// a widget
+
+		// NOTE: caching lower-cased args in the prototype is only 
+		// acceptable if the properties are invariant.
+		// if we have a name-cache, get it
+		var lcArgs = dojo.widget.lcArgsCache[this.widgetType];
+		if ( lcArgs == null ){
+			// build a lower-case property name cache if we don't have one
+			lcArgs = {};
+			for(var y in this){
+				lcArgs[((new String(y)).toLowerCase())] = y;
+			}
+			dojo.widget.lcArgsCache[this.widgetType] = lcArgs;
+		}
+		var visited = {};
+		for(var x in args){
+			if(!this[x]){ // check the cache for properties
+				var y = lcArgs[(new String(x)).toLowerCase()];
+				if(y){
+					args[y] = args[x];
+					x = y; 
+				}
+			}
+			if(visited[x]){ continue; }
+			visited[x] = true;
+			if((typeof this[x]) != (typeof undef)){
+				if(typeof args[x] != "string"){
+					this[x] = args[x];
+				}else{
+					if(dojo.lang.isString(this[x])){
+						this[x] = args[x];
+					}else if(dojo.lang.isNumber(this[x])){
+						this[x] = new Number(args[x]); // FIXME: what if NaN is the result?
+					}else if(dojo.lang.isBoolean(this[x])){
+						this[x] = (args[x].toLowerCase()=="false") ? false : true;
+					}else if(dojo.lang.isFunction(this[x])){
+
+						// FIXME: need to determine if always over-writing instead
+						// of attaching here is appropriate. I suspect that we
+						// might want to only allow attaching w/ action items.
+						
+						// RAR, 1/19/05: I'm going to attach instead of
+						// over-write here. Perhaps function objects could have
+						// some sort of flag set on them? Or mixed-into objects
+						// could have some list of non-mutable properties
+						// (although I'm not sure how that would alleviate this
+						// particular problem)? 
+
+						// this[x] = new Function(args[x]);
+
+						// after an IRC discussion last week, it was decided
+						// that these event handlers should execute in the
+						// context of the widget, so that the "this" pointer
+						// takes correctly.
+						
+						// argument that contains no punctuation other than . is 
+						// considered a function spec, not code
+						if(args[x].search(/[^\w\.]+/i) == -1){
+							this[x] = dojo.evalObjPath(args[x], false);
+						}else{
+							var tn = dojo.lang.nameAnonFunc(new Function(args[x]), this);
+							dojo.event.connect(this, x, this, tn);
+						}
+					}else if(dojo.lang.isArray(this[x])){ // typeof [] == "object"
+						this[x] = args[x].split(";");
+					} else if (this[x] instanceof Date) {
+						this[x] = new Date(Number(args[x])); // assume timestamp
+					}else if(typeof this[x] == "object"){ 
+						// FIXME: should we be allowing extension here to handle
+						// other object types intelligently?
+
+						// if we defined a URI, we probablt want to allow plain strings
+						// to override it
+						if (this[x] instanceof dojo.uri.Uri){
+
+							this[x] = args[x];
+						}else{
+
+							// FIXME: unlike all other types, we do not replace the
+							// object with a new one here. Should we change that?
+							var pairs = args[x].split(";");
+							for(var y=0; y<pairs.length; y++){
+								var si = pairs[y].indexOf(":");
+								if((si != -1)&&(pairs[y].length>si)){
+									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<this.children.length; x++){
+			if(this.children[x] === widget){
+				this.children.splice(x, 1);
+				break;
+			}
+		}
+		return widget;
+	},
+
+	resize: function(width, height){
+		// both width and height may be set as percentages. The setWidth and
+		// setHeight  functions attempt to determine if the passed param is
+		// specified in percentage or native units. Integers without a
+		// measurement are assumed to be in the native unit of measure.
+		this.setWidth(width);
+		this.setHeight(height);
+	},
+
+	setWidth: function(width){
+		if((typeof width == "string")&&(width.substr(-1) == "%")){
+			this.setPercentageWidth(width);
+		}else{
+			this.setNativeWidth(width);
+		}
+	},
+
+	setHeight: function(height){
+		if((typeof height == "string")&&(height.substr(-1) == "%")){
+			this.setPercentageHeight(height);
+		}else{
+			this.setNativeHeight(height);
+		}
+	},
+
+	setPercentageHeight: function(height){
+		// SUBCLASSES MUST IMPLEMENT
+		return false;
+	},
+
+	setNativeHeight: function(height){
+		// SUBCLASSES MUST IMPLEMENT
+		return false;
+	},
+
+	setPercentageWidth: function(width){
+		// SUBCLASSES MUST IMPLEMENT
+		return false;
+	},
+
+	setNativeWidth: function(width){
+		// SUBCLASSES MUST IMPLEMENT
+		return false;
+	},
+
+	getPreviousSibling: function() {
+		var idx = this.getParentIndex();
+ 
+		 // first node is idx=0 not found is idx<0
+		if (idx<=0) return null;
+ 
+		return this.getSiblings()[idx-1];
+	},
+ 
+	getSiblings: function() {
+		return this.parent.children;
+	},
+ 
+	getParentIndex: function() {
+		return dojo.lang.indexOf( this.getSiblings(), this, true);
+	},
+ 
+	getNextSibling: function() {
+ 
+		var idx = this.getParentIndex();
+ 
+		if (idx == this.getSiblings().length-1) return null; // last node
+		if (idx < 0) return null; // not found
+ 
+		return this.getSiblings()[idx+1];
+ 
+	}
+});
+
+// Lower case name cache: listing of the lower case elements in each widget.
+// We can't store the lcArgs in the widget itself because if B subclasses A,
+// then B.prototype.lcArgs might return A.prototype.lcArgs, which is not what we
+// want
+dojo.widget.lcArgsCache = {};
+
+// TODO: should have a more general way to add tags or tag libraries?
+// TODO: need a default tags class to inherit from for things like getting propertySets
+// TODO: parse properties/propertySets into component attributes
+// TODO: parse subcomponents
+// TODO: copy/clone raw markup fragments/nodes as appropriate
+dojo.widget.tags = {};
+dojo.widget.tags.addParseTreeHandler = function(type){
+	var ltype = type.toLowerCase();
+	this[ltype] = function(fragment, widgetParser, parentComp, insertionIndex, localProps){ 
+		return dojo.widget.buildWidgetFromParseTree(ltype, fragment, widgetParser, parentComp, insertionIndex, localProps);
+	}
+}
+dojo.widget.tags.addParseTreeHandler("dojo:widget");
+
+dojo.widget.tags["dojo:propertyset"] = function(fragment, widgetParser, parentComp){
+	// FIXME: Is this needed?
+	// FIXME: Not sure that this parses into the structure that I want it to parse into...
+	// FIXME: add support for nested propertySets
+	var properties = widgetParser.parseProperties(fragment["dojo:propertyset"]);
+}
+
+// FIXME: need to add the <dojo:connect />
+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){
+	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["dojo:"+stype]);
+	// var tic = new Date();
+	var twidget = dojo.widget.manager.getImplementation(stype);
+	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 loose no less than 5ms in construction!
+	var ret = twidget.create(localProperties, frag, parentComp);
+	// dojo.debug(new Date() - tic);
+	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);
+	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);
+}
\ No newline at end of file


Property changes on: trunk/PhotoOp/root/static/js/src/widget/Widget.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/Wizard.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/Wizard.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/Wizard.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,210 @@
+/*
+	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.Wizard");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.LayoutContainer");
+dojo.require("dojo.widget.ContentPane");
+dojo.require("dojo.event.*");
+dojo.require("dojo.html");
+dojo.require("dojo.style");
+
+//////////////////////////////////////////
+// WizardContainer -- a set of panels
+//////////////////////////////////////////
+dojo.widget.WizardContainer = function() {
+	dojo.widget.html.LayoutContainer.call(this);
+}
+dojo.inherits(dojo.widget.WizardContainer, dojo.widget.html.LayoutContainer);
+
+dojo.lang.extend(dojo.widget.WizardContainer, {
+
+	widgetType: "WizardContainer",
+
+	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();
+	}
+});
+dojo.widget.tags.addParseTreeHandler("dojo:WizardContainer");
+
+//////////////////////////////////////////
+// WizardPane -- a panel in a wizard
+//////////////////////////////////////////
+dojo.widget.WizardPane = function() {
+	dojo.widget.html.ContentPane.call(this);
+}
+dojo.inherits(dojo.widget.WizardPane, dojo.widget.html.ContentPane);
+
+dojo.lang.extend(dojo.widget.WizardPane, {
+	widgetType: "WizardPane",
+
+	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();
+		}
+	}
+});
+
+dojo.widget.tags.addParseTreeHandler("dojo:WizardPane");


Property changes on: trunk/PhotoOp/root/static/js/src/widget/Wizard.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/YahooMap.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/YahooMap.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/YahooMap.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,27 @@
+/*
+	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.YahooMap");
+dojo.provide("dojo.widget.YahooMap.Controls");
+dojo.require("dojo.widget.*");
+
+dojo.widget.defineWidget(
+	"dojo.widget.YahooMap",
+	dojo.widget.Widget,
+	{ isContainer: false }
+);
+
+dojo.widget.YahooMap.Controls={
+	MapType:"maptype",
+	Pan:"pan",
+	ZoomLong:"zoomlong",
+	ZoomShort:"zoomshort"
+};
+dojo.requireAfterIf("html", "dojo.widget.html.YahooMap");


Property changes on: trunk/PhotoOp/root/static/js/src/widget/YahooMap.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/__package__.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/__package__.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/__package__.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,23 @@
+/*
+	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.kwCompoundRequire({
+	common: ["dojo.xml.Parse", 
+			 "dojo.widget.Widget", 
+			 "dojo.widget.Parse", 
+			 "dojo.widget.Manager"],
+	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.*");


Property changes on: trunk/PhotoOp/root/static/js/src/widget/__package__.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/demoEngine/DemoContainer.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/demoEngine/DemoContainer.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/demoEngine/DemoContainer.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,107 @@
+/*
+	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.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";
+	}
+);


Property changes on: trunk/PhotoOp/root/static/js/src/widget/demoEngine/DemoContainer.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/demoEngine/DemoItem.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/demoEngine/DemoItem.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/demoEngine/DemoItem.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,71 @@
+/*
+	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.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";
+	}
+);


Property changes on: trunk/PhotoOp/root/static/js/src/widget/demoEngine/DemoItem.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/demoEngine/DemoNavigator.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/demoEngine/DemoNavigator.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/demoEngine/DemoNavigator.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,188 @@
+/*
+	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.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 = [];
+	}
+);


Property changes on: trunk/PhotoOp/root/static/js/src/widget/demoEngine/DemoNavigator.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/demoEngine/DemoPane.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/demoEngine/DemoPane.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/demoEngine/DemoPane.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,44 @@
+/*
+	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.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 = "";
+	}
+);


Property changes on: trunk/PhotoOp/root/static/js/src/widget/demoEngine/DemoPane.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/demoEngine/SourcePane.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/demoEngine/SourcePane.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/demoEngine/SourcePane.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,52 @@
+/*
+	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.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 = "";
+	}
+);


Property changes on: trunk/PhotoOp/root/static/js/src/widget/demoEngine/SourcePane.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/demoEngine/__package__.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/demoEngine/__package__.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/demoEngine/__package__.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,20 @@
+/*
+	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.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.*");


Property changes on: trunk/PhotoOp/root/static/js/src/widget/demoEngine/__package__.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/DemoContainer.css
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/DemoContainer.css	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/DemoContainer.css	2009-02-07 13:12:54 UTC (rev 5416)
@@ -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;
+}
+


Property changes on: trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/DemoContainer.css
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/DemoContainer.html
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/DemoContainer.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/DemoContainer.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,25 @@
+<div dojoAttachPoint="domNode">
+	<table width="100%" cellspacing="0" cellpadding="5">
+		<tbody>
+			<tr dojoAttachPoint="headerNode">
+				<td dojoAttachPoint="returnNode" valign="middle" width="1%">
+					<img dojoAttachPoint="returnImageNode" dojoAttachEvent="onclick: returnToDemos"/>
+				</td>
+				<td>
+					<h1 dojoAttachPoint="demoNameNode"></h1>
+					<p dojoAttachPoint="summaryNode"></p>
+				</td>
+				<td dojoAttachPoint="tabControlNode" valign="middle" align="right" nowrap>
+					<span dojoAttachPoint="sourceButtonNode" dojoAttachEvent="onclick: showSource">source</span>
+					<span dojoAttachPoint="demoButtonNode" dojoAttachEvent="onclick: showDemo">demo</span>
+				</td>
+			</tr>
+			<tr>
+				<td colspan="3">
+					<div dojoAttachPoint="tabNode">
+					</div>
+				</td>
+			</tr>
+		</tbody>
+	</table>
+</div>


Property changes on: trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/DemoContainer.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/DemoItem.css
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/DemoItem.css	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/DemoItem.css	2009-02-07 13:12:54 UTC (rev 5416)
@@ -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;
+}


Property changes on: trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/DemoItem.css
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/DemoItem.html
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/DemoItem.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/DemoItem.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,21 @@
+<div dojoAttachPoint="domNode">
+	<div dojoAttachPoint="summaryBoxNode">
+		<table width="100%" cellspacing="0" cellpadding="0">
+			<tbody>
+				<tr>
+					<td dojoAttachPoint="screenshotTdNode" valign="top" width="1%">
+						<img dojoAttachPoint="thumbnailImageNode" dojoAttachEvent="onclick: onSelectDemo" />
+					</td>
+					<td dojoAttachPoint="summaryContainerNode" valign="top">
+						<h1 dojoAttachPoint="nameNode">
+						</h1>
+						<div dojoAttachPoint="summaryNode">
+							<p dojoAttachPoint="descriptionNode"></p>
+							<div dojoAttachPoint="viewDemoLinkNode"><img dojoAttachPoint="viewDemoImageNode"/ dojoAttachEvent="onclick: onSelectDemo"></div>
+						</div>
+					</td>
+				</tr>
+			</tbody>
+		</table>
+	</div>
+</div>


Property changes on: trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/DemoItem.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/DemoNavigator.css
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/DemoNavigator.css	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/DemoNavigator.css	2009-02-07 13:12:54 UTC (rev 5416)
@@ -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;
+}


Property changes on: trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/DemoNavigator.css
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/DemoNavigator.html
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/DemoNavigator.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/DemoNavigator.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,24 @@
+<div dojoAttachPoint="domNode">
+	<table width="100%" cellspacing="0" cellpadding="5">
+		<tbody>
+			<tr dojoAttachPoint="navigationContainer">
+				<td dojoAttachPoint="categoriesNode" valign="top" width="1%">
+					<h1>Categories</h1>
+					<div dojoAttachPoint="categoriesButtonsNode"></div>
+				</td>
+
+				<td dojoAttachPoint="demoListNode" valign="top">
+					<div dojoAttachPoint="demoListWrapperNode">
+						<div dojoAttachPoint="demoListContainerNode">
+						</div>
+					</div>
+				</td>
+			</tr>
+			<tr>
+				<td colspan="2">
+					<div dojoAttachPoint="demoNode"></div>
+				</td>
+			</tr>
+		</tbody>
+	</table>
+</div>


Property changes on: trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/DemoNavigator.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/DemoPane.css
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/DemoPane.css	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/DemoPane.css	2009-02-07 13:12:54 UTC (rev 5416)
@@ -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;
+}


Property changes on: trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/DemoPane.css
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/DemoPane.html
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/DemoPane.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/DemoPane.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,3 @@
+<div dojoAttachPoint="domNode">
+	<iframe dojoAttachPoint="demoNode"></iframe>
+</div>


Property changes on: trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/DemoPane.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/SourcePane.css
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/SourcePane.css	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/SourcePane.css	2009-02-07 13:12:54 UTC (rev 5416)
@@ -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;
+}


Property changes on: trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/SourcePane.css
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/SourcePane.html
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/SourcePane.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/SourcePane.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,3 @@
+<div dojoAttachPoint="domNode">
+	<textarea dojoAttachPoint="sourceNode" rows="100%"></textarea>
+</div>


Property changes on: trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/SourcePane.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/general.css
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/general.css	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/general.css	2009-02-07 13:12:54 UTC (rev 5416)
@@ -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;
+}


Property changes on: trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/general.css
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/images/test_thumb.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/images/test_thumb.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/images/viewDemo.png
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/demoEngine/templates/images/viewDemo.png
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/html/AccordionPane.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/html/AccordionPane.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/html/AccordionPane.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -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<siblings.length; x++) {
+			if (siblings[x].widgetType==this.widgetType) {
+				if (this.domNode != siblings[x]) {
+					var ap = dojo.widget.byNode(siblings[x]);
+					this.siblingWidgets.push(ap);
+					height -= ap.getCollapsedHeight();
+				}
+			}
+		}
+	
+		for (var x=0; x<this.siblingWidgets.length; x++) {
+			dojo.style.setOuterHeight(this.siblingWidgets[x].containerNode,height);
+		}
+
+		dojo.style.setOuterHeight(this.containerNode,height);
+	},
+
+	onLabelClick: function() {
+		this.setSizes();
+		if (!this.open) { 
+			for (var x=0; x<this.siblingWidgets.length;x++) {
+				if (this.siblingWidgets[x].open) {
+					this.siblingWidgets[x].collapse();
+				}
+			}
+			this.expand();
+		} else {
+			if (this.allowCollapse) {
+				this.collapse();
+			}
+		}
+	}
+});
+
+dojo.widget.tags.addParseTreeHandler("dojo:AccordionPane");


Property changes on: trunk/PhotoOp/root/static/js/src/widget/html/AccordionPane.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/html/Button.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/html/Button.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/html/Button.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,281 @@
+/*
+	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.Button");
+dojo.require("dojo.lang.extras");
+dojo.require("dojo.html");
+dojo.require("dojo.style");
+
+dojo.require("dojo.widget.HtmlWidget");
+dojo.require("dojo.widget.Button");
+
+dojo.widget.html.Button = function(){
+	// call superclass constructors
+	dojo.widget.HtmlWidget.call(this);
+	dojo.widget.Button.call(this);
+}
+dojo.inherits(dojo.widget.html.Button, dojo.widget.HtmlWidget);
+dojo.lang.extend(dojo.widget.html.Button, dojo.widget.Button.prototype);
+dojo.lang.extend(dojo.widget.html.Button, {
+
+	templatePath: dojo.uri.dojoUri("src/widget/templates/HtmlButtonTemplate.html"),
+	templateCssPath: dojo.uri.dojoUri("src/widget/templates/HtmlButtonTemplate.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);
+		if ( this.disabled ) {
+			dojo.html.prependClass(this.domNode, "dojoButtonDisabled");
+		}
+		
+		// after the browser has had a little time to calculate the size needed
+		// for the button contents, size the button
+		dojo.lang.setTimeout(this, this.sizeMyself, 0);
+	},
+
+	sizeMyself: function(e){
+		this.height = dojo.style.getOuterHeight(this.containerNode);
+		this.containerWidth = dojo.style.getOuterWidth(this.containerNode);
+		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);
+			
+		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; }
+		dojo.html.removeClass(this.domNode, "dojoButtonHover");
+		this._setImage(this.inactiveImg);
+	},
+
+	buttonClick: function(e){
+		if( !this.disabled && this.onClick ) { this.onClick(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.style.getAbsolutePosition(this.domNode, false);
+			menu.open(pos.x, pos.y+this.height, this);
+		} else if ( menu.close && menu.isShowingNow ){
+			menu.close();
+		} else {
+			menu.toggle();
+		}
+	},
+	
+	onParentResized: function(){
+		// Not sure why this is necessary; but if button is inside a hidden floating
+		// pane (see Mail.html demo).  Revisit when buttons are redesigned
+		this.sizeMyself();
+	}
+});
+
+/**** DropDownButton - push the button and a menu shows up *****/
+dojo.widget.html.DropDownButton = function(){
+	// call constructors of superclasses
+	dojo.widget.DropDownButton.call(this);
+	dojo.widget.html.Button.call(this);
+}
+dojo.inherits(dojo.widget.html.DropDownButton, dojo.widget.html.Button);
+dojo.lang.extend(dojo.widget.html.DropDownButton, dojo.widget.DropDownButton.prototype);
+
+dojo.lang.extend(dojo.widget.html.DropDownButton, {
+
+	downArrow: "src/widget/templates/images/whiteDownArrow.gif",
+	disabledDownArrow: "src/widget/templates/images/whiteDownArrow.gif",
+
+	fillInTemplate: function(args, frag){
+		dojo.widget.html.DropDownButton.superclass.fillInTemplate.call(this, args, frag);
+
+		// draw the arrow
+		var arrow = document.createElement("img");
+		arrow.src = dojo.uri.dojoUri(this.disabled ? this.disabledDownArrow : this.downArrow);
+		dojo.html.setClass(arrow, "downArrow");
+		this.containerNode.appendChild(arrow);
+	},
+
+	onClick: function (e){
+		if( this.disabled ){ return; }
+		this._toggleMenu(this.menuId);
+	}
+});
+
+/**** ComboButton - left side is normal button, right side shows menu *****/
+dojo.widget.html.ComboButton = function(){
+	// call constructors of superclasses
+	dojo.widget.html.Button.call(this);
+	dojo.widget.ComboButton.call(this);
+}
+dojo.inherits(dojo.widget.html.ComboButton, dojo.widget.html.Button);
+dojo.lang.extend(dojo.widget.html.ComboButton, dojo.widget.ComboButton.prototype);
+dojo.lang.extend(dojo.widget.html.ComboButton, {
+
+	templatePath: dojo.uri.dojoUri("src/widget/templates/HtmlComboButtonTemplate.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
+
+	sizeMyself: function(e){
+		this.height = dojo.style.getOuterHeight(this.containerNode);
+		this.containerWidth = dojo.style.getOuterWidth(this.containerNode);
+		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 ) {
+			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");
+	}
+});

Added: trunk/PhotoOp/root/static/js/src/widget/html/Button2.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/html/Button2.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/html/Button2.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,25 @@
+/*
+	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 stub that will be removed in 0.4, see ../Button2.html for details
+
+dojo.provide("dojo.widget.html.Button2");
+
+dojo.widget.html.Button2 = function(){}
+dojo.inherits(dojo.widget.html.Button2, dojo.widget.html.Button);
+dojo.lang.extend(dojo.widget.html.Button2, { widgetType: "Button2" });
+
+dojo.widget.html.DropDownButton2 = function(){}
+dojo.inherits(dojo.widget.html.DropDownButton2, dojo.widget.html.DropDownButton);
+dojo.lang.extend(dojo.widget.html.DropDownButton2, { widgetType: "DropDownButton2" });
+
+dojo.widget.html.ComboButton2 = function(){}
+dojo.inherits(dojo.widget.html.ComboButton2, dojo.widget.html.ComboButton);
+dojo.lang.extend(dojo.widget.html.ComboButton2, { widgetType: "ComboButton2" });
\ No newline at end of file


Property changes on: trunk/PhotoOp/root/static/js/src/widget/html/Button2.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/html/Checkbox.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/html/Checkbox.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/html/Checkbox.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,82 @@
+/*
+	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.Checkbox");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.event");
+dojo.require("dojo.html");
+
+dojo.widget.defineWidget(
+	"dojo.widget.html.Checkbox",
+	dojo.widget.HtmlWidget,
+	{
+		widgetType: "Checkbox",
+	
+		templatePath: dojo.uri.dojoUri('src/widget/templates/HtmlCheckBox.html'),
+
+		srcOn: dojo.uri.dojoUri('src/widget/templates/check_on.gif'),
+		srcOff: dojo.uri.dojoUri('src/widget/templates/check_off.gif'),
+		srcDisabledOn: dojo.uri.dojoUri('src/widget/templates/check_disabled_on.gif'),
+		srcDisabledOff: dojo.uri.dojoUri('src/widget/templates/check_disabled_off.gif'),
+		srcHoverOn: dojo.uri.dojoUri('src/widget/templates/check_hover_on.gif'),
+		srcHoverOff: dojo.uri.dojoUri('src/widget/templates/check_hover_off.gif'),
+
+		imgSrc: null,
+
+		// parameters
+		disabled: "enabled",
+		name: "",
+		checked: false,
+		tabIndex: -1,
+
+		imgNode: null,
+		inputNode: null,
+
+		postMixInProperties: function(){
+			// set correct source for image before instantiating template
+			this._updateImgSrc();
+		},
+
+		onMouseUp: function(){
+			if(this.disabled == "enabled"){
+				this.checked = !this.checked;
+				this.inputNode.checked = this.checked;
+				this._updateImgSrc();
+			}
+		},
+
+		onMouseOver: function(){
+			this.hover=true;
+			this._updateImgSrc();
+		},
+
+		onMouseOut: function(){
+			this.hover=false;
+			this._updateImgSrc();
+		},
+
+		_updateImgSrc: function(){
+			if(this.disabled == "enabled"){
+				if(this.hover){
+					this.imgSrc = this.checked ? this.srcHoverOn : this.srcHoverOff;
+				}else{
+					this.imgSrc = this.checked ? this.srcOn : this.srcOff;
+				}
+			}else{
+				this.imgSrc = this.checked ? this.srcDisabledOn : this.srcDisabledOff;
+			}
+			if(this.imgNode){
+				this.imgNode.src = this.imgSrc;
+			}
+		}
+	}
+);
+


Property changes on: trunk/PhotoOp/root/static/js/src/widget/html/Checkbox.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/html/ComboBox.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/html/ComboBox.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/html/ComboBox.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,611 @@
+/*
+	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.ComboBox");
+dojo.require("dojo.widget.ComboBox");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.io.*");
+dojo.require("dojo.lfx.*");
+dojo.require("dojo.dom");
+dojo.require("dojo.html");
+dojo.require("dojo.string");
+dojo.require("dojo.widget.html.stabile");
+
+dojo.widget.defineWidget(
+	"dojo.widget.html.ComboBox",
+	[dojo.widget.HtmlWidget, dojo.widget.ComboBox],
+	{
+		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,
+		_result_list_open: false,
+		_gotFocus: false,
+		_mouseover_list: false,
+		dataProviderClass: "dojo.widget.ComboBoxDataProvider",
+
+		templatePath: dojo.uri.dojoUri("src/widget/templates/HtmlComboBox.html"),
+		templateCssPath: dojo.uri.dojoUri("src/widget/templates/HtmlComboBox.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);
+		},
+	
+		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;
+			}
+			switch(keyCode){
+	 			case k.KEY_DOWN_ARROW:
+					if(!this._result_list_open){
+						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._result_list_open){
+						dojo.event.browser.stopEvent(evt);
+					}
+					// fallthrough
+				case k.KEY_TAB:
+					// using linux alike tab for autocomplete
+					if(!this.autoComplete && this._result_list_open && this._highlighted_option){
+						dojo.event.browser.stopEvent(evt);
+						this.selectOption({ 'target': this._highlighted_option, 'noHide': true });
+	
+						// put caret last
+						this.setSelectedRange(this.textInputNode, this.textInputNode.value.length, null);
+					}else{
+						this.selectOption();
+						return;
+					}
+					break;
+				case k.KEY_SPACE:
+					if(this._result_list_open && 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);
+			}
+		},
+	
+		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);
+		},
+	
+		// opera, khtml, safari doesnt support node.scrollIntoView(), workaround
+		scrollIntoView: function(){
+			var node = this._highlighted_option;
+			var parent = this.optionsListNode;
+			// don't rely on that node.scrollIntoView works just because the function is there
+			// it doesnt work in Konqueror or Opera even though the function is there and probably
+			// not safari either
+			// dont like browser sniffs implementations but sometimes you have to use it
+			if(dojo.render.html.ie || dojo.render.html.mozilla){
+				// IE, mozilla
+				node.scrollIntoView(false);	
+			}else{
+				var parentBottom = parent.scrollTop + dojo.style.getInnerHeight(parent);
+				var nodeBottom = node.offsetTop + dojo.style.getOuterHeight(node);
+				if(parentBottom < nodeBottom){
+					parent.scrollTop += (nodeBottom - parentBottom);
+				}else if(parent.scrollTop > 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);
+			}
+		}
+	}
+);


Property changes on: trunk/PhotoOp/root/static/js/src/widget/html/ComboBox.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/html/ContentPane.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/html/ContentPane.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/html/ContentPane.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -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(/<title[^>]*>([\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(/<title[^>]*>[\s\S]*?<\/title>/i, "");
+		}
+
+		// cut out <style> url(...) </style>, as that bails out in khtml
+		var match = [];
+		while(match){
+			match = s.match(/<style[^>]*>([\s\S]*?)<\/style>/i);
+			if(!match){ break; }
+			styles.push(dojo.style.fixPathsInCssText(match[1], url));
+			s = s.replace(/<style[^>]*?>[\s\S]*?<\/style>/i, "");
+		}
+
+		// 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 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(/<script([^>]*)>([\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(/<script[^>]*>[\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 <link rel="stylesheet" href="..">
+		match = [];
+		while(match){
+			match = s.match(/<link ([^>]*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(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im);
+				if(matches) { data.xml = matches[1]; }
+			}
+			// insert styleNodes, from <style>....
+			for(var i = 0; i < data.styles.length; i++){
+				if(i==0){ 
+					this._remoteStyles = []; 
+				}
+				this._remoteStyles.push(dojo.style.insertCssText(data.styles[i]));
+			}
+			// insert styleNodes, from <link href="...">
+			for(var i = 0; i < data.linkStyles.length; i++){
+				if(i==0){ 
+					this._remoteStyles = []; 
+				}
+				this._remoteStyles.push(dojo.style.insertCssFile(data.linkStyles[i]));
+			}
+			this._setContent(data.xml);
+
+			if(this.parseContent){
+				for(var i = 0; i < data.requires.length; i++){
+					try{ 
+						eval(data.requires[i]);
+					} catch(e){
+						this._handleDefaults(e, "onContentError", true);
+					}
+				}
+			}
+			// need to allow async load, Xdomain uses it
+			// is inline function because we cant send args to addOnLoad function
+			var _self = this;
+			function asyncParse(){
+				if(_self.executeScripts){
+					_self._executeScripts(data);
+				}
+
+				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() {
+		if(dojo.lang.isFunction(this.handler)) {
+			this.handler(this, this.domNode);
+			return false;
+		}
+		return true;
+	},
+
+	_executeScripts: function(data) {
+		// do remoteScripts first
+		var self = this;
+		for(var i = 0; i < data.remoteScripts.length; i++){
+			dojo.io.bind({
+				"url": data.remoteScripts[i],
+				"useCash":	this.cacheContent,
+				"load":     function(type, scriptStr){
+						dojo.lang.hitch(self, data.scripts.push(scriptStr));
+				},
+				"error":    function(type, error){
+						self._handleDefaults.call(self, type + " downloading remote script", "onExecError", true);
+				},
+				"mimetype": "text/plain",
+				"sync":     true
+			});
+		}
+
+		var scripts = "";
+		for(var i = 0; i < data.scripts.length; i++){
+			scripts += data.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..
+			this.scriptScope = null;
+			this.scriptScope = new (new Function('_container_', scripts+'; return this;'))(self);
+		}catch(e){
+			this._handleDefaults("Error running scripts from content:\n"+e, "onExecError", true);
+		}
+	}
+});
+
+dojo.widget.tags.addParseTreeHandler("dojo:ContentPane");


Property changes on: trunk/PhotoOp/root/static/js/src/widget/html/ContentPane.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/html/ContextMenu.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/html/ContextMenu.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/html/ContextMenu.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,166 @@
+/*
+	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.ContextMenu");
+dojo.require("dojo.html");
+dojo.require("dojo.widget.HtmlWidget");
+dojo.require("dojo.widget.ContextMenu");
+dojo.require("dojo.lang");
+
+dojo.widget.html.ContextMenu = function(){
+	dojo.widget.ContextMenu.call(this);
+	dojo.widget.HtmlWidget.call(this);
+
+	this.isShowing = 0;
+	this.templatePath = dojo.uri.dojoUri("src/widget/templates/HtmlContextMenuTemplate.html");
+	this.templateCssPath = dojo.uri.dojoUri("src/widget/templates/Menu.css");
+
+	this.targetNodeIds = []; // fill this with nodeIds upon widget creation and it only responds to those nodes
+
+	// default event detection method 
+	var eventType = "oncontextmenu"; 
+
+	var doc = document.documentElement  || document.body; 
+
+	var _blockHide = false; 
+
+	this.fillInTemplate = function(args, frag){
+
+		var func = "onOpen";
+		var attached = false;
+
+		// connect with rightclick if oncontextmenu is not around
+		// NOTE: It would be very nice to have a dojo.event.browser.supportsEvent here
+		// NOTE: Opera does not have rightclick events, it is listed here only because
+		//     it bails out when connecting with oncontextmenu event
+
+		if((dojo.render.html.khtml && !dojo.render.html.safari) || (dojo.render.html.opera)){
+			eventType = "onmousedown";
+			func = "_checkRightClick";
+		}
+
+		// attach event listeners to our selected nodes
+		for(var i=0; i<this.targetNodeIds.length; i++){
+			var node = document.getElementById(this.targetNodeIds[i]);
+			if(node){
+				dojo.event.connect(node, eventType, this, func);
+				attached = true;
+			}else{
+				// remove this nodeId
+				dojo.debug("Couldent find "+this.targetNodeIds[i]+", cant do ContextMenu on this node");
+				this.targetNodeIds.splice(i,1);
+			}
+		}
+
+		// if we got attached to a node, hide on all non node contextevents
+		if(attached){ func = "_canHide"; }
+
+		dojo.event.connect(doc, eventType, this, func);
+	}
+
+	this.onOpen = function(evt){
+		// if (this.isShowing){ this.onHide(evt); } // propably not needed
+		this.isShowing = 1;
+
+		// if I do this, I cant preventDefault in khtml
+		//evt = dojo.event.browser.fixEvent(evt);
+ 
+		// stop default contextmenu, needed in khtml
+		if (evt.preventDefault){ evt.preventDefault(); }
+
+		// need to light up this one before we check width and height
+		this.domNode.style.left = "-9999px";
+		this.domNode.style.top  = "-9999px";
+		this.domNode.style.display = "block";
+
+		// calculate if menu is going to apear within window
+		// or if its partially out of visable area
+		with(dojo.html){
+
+			var menuW = getInnerWidth(this.domNode);
+			var menuH = getInnerHeight(this.domNode);
+
+			var viewport = getViewportSize();
+			var scrolloffset = getScrollOffset();
+		}
+
+		var minX = viewport[0];
+		var minY = viewport[1];
+
+		var maxX = (viewport[0] + scrolloffset[0]) - menuW;
+		var maxY = (viewport[1] + scrolloffset[1]) - menuH;
+
+		var posX = evt.clientX + scrolloffset[0];
+		var posY = evt.clientY + scrolloffset[1];
+
+		if (posX > maxX){ posX = posX - menuW; }
+		if (posY > maxY){ posY = posY - menuH; }
+
+		this.domNode.style.left = posX + "px";
+		this.domNode.style.top = posY + "px";
+
+
+		// block the onclick that follows this particular right click
+		// not if the eventtrigger is documentElement and always when
+		// we use onmousedown hack
+		_blockHide = (evt.currentTarget!=doc || eventType=='onmousedown');
+
+		//return false; // we propably doesnt need to return false as we dont stop the event as we did before
+	}
+
+	/*
+	* _canHide is meant to block the onHide call that follows the event that triggered
+	* onOpen. This is (hopefully) faster that event.connect and event.disconnect every
+	* time the code executes and it makes connecting with onmousedown event possible
+	* and we dont have to stop the event from bubbling further.
+	*
+	* this code is moved into a separete function because it makes it possible for the
+	* user to connect to a onHide event, if anyone would like that.
+	*/
+
+	this._canHide = function(evt){
+		// block the onclick that follows the same event that turn on contextmenu
+		if(_blockHide){
+			// the onclick check is needed to prevent displaying multiple
+			// menus when we have 2 or more contextmenus loaded and are using
+			// the onmousedown hack
+			if(evt.type=='click' || eventType=='oncontextmenu'){
+				_blockHide = false;
+				return;
+			}else{
+				return;
+			}
+		}
+
+		this.onHide(evt);
+	}
+	
+	this.onHide = function(evt){
+		// FIXME: use whatever we use to do more general style setting?
+		this.domNode.style.display = "none";
+		//dojo.event.disconnect(doc, "onclick", this, "onHide");
+		this.isShowing = 0;
+	}
+
+	// callback for rightclicks, needed for browsers that doesnt implement oncontextmenu, konqueror and more? 
+	this._checkRightClick = function(evt){ 
+
+		// for some reason konq comes here even when we are not clicking on the attached nodes 
+		// added check for targetnode 
+		if (evt.button==2 && (this.targetNodeIds.length==0 || (evt.currentTarget.id!="" && dojo.lang.inArray(this.targetNodeIds, evt.currentTarget.id)))){
+
+			return this.onOpen(evt);
+		}
+	}
+
+	dojo.event.connect(doc, "onclick", this, "_canHide");
+}
+
+dojo.inherits(dojo.widget.html.ContextMenu, dojo.widget.HtmlWidget);


Property changes on: trunk/PhotoOp/root/static/js/src/widget/html/ContextMenu.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/html/DatePicker.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/html/DatePicker.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/html/DatePicker.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,346 @@
+/*
+	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.DatePicker");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.HtmlWidget");
+dojo.require("dojo.widget.DatePicker");
+dojo.require("dojo.event.*");
+dojo.require("dojo.html");
+dojo.require("dojo.date");
+
+/*
+	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.html.DatePicker",
+	dojo.widget.HtmlWidget,
+	{
+		classConstructor: function() {
+			// mixin dojo.widget.DatePicker non-demoninational code
+			dojo.widget.DatePicker.call(this);
+			// 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 = {};
+		},
+		classNames: {
+			previous: "previousMonth",
+			current: "currentMonth",
+			next: "nextMonth",
+			currentDate: "currentDate",
+			selectedDate: "selectedItem"
+		},
+		templatePath:  dojo.uri.dojoUri("src/widget/templates/HtmlDatePicker.html"),
+		templateCssPath:  dojo.uri.dojoUri("src/widget/templates/HtmlDatePicker.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() {
+			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.html.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.date.months[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) {
+			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(evt.target, this.classNames["next"])) {
+				month = ++month % 12;
+				// if month is now == 0, add a year
+				year = (month==0) ? ++year : year;
+			} else if (dojo.html.hasClass(evt.target, 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, evt.target.innerHTML);
+			this.setDate(dojo.widget.DatePicker.util.toRfcDate(this.date));
+			this.initUI();
+		}
+	}
+);


Property changes on: trunk/PhotoOp/root/static/js/src/widget/html/DatePicker.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/html/DebugConsole.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/html/DebugConsole.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/html/DebugConsole.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,31 @@
+/*
+	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.DebugConsole");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.FloatingPane");
+
+dojo.widget.html.DebugConsole= function(){
+
+	dojo.widget.html.FloatingPane.call(this);
+	dojo.widget.DebugConsole.call(this);
+}
+
+dojo.inherits(dojo.widget.html.DebugConsole, dojo.widget.html.FloatingPane);
+
+dojo.lang.extend(dojo.widget.html.DebugConsole, {
+	fillInTemplate: function() {
+		dojo.widget.html.DebugConsole.superclass.fillInTemplate.apply(this, arguments);
+		this.containerNode.id = "debugConsoleClientPane";
+		djConfig.isDebug = true;
+		djConfig.debugContainerId = this.containerNode.id;
+	}
+});


Property changes on: trunk/PhotoOp/root/static/js/src/widget/html/DebugConsole.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/html/DemoEngine.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/html/DemoEngine.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/html/DemoEngine.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,293 @@
+/*
+	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.DemoEngine");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.HtmlWidget");
+dojo.require("dojo.lfx.*");
+dojo.require("dojo.style");
+dojo.require("dojo.io.*");
+dojo.require("dojo.widget.Button");
+dojo.require("dojo.widget.TabContainer");
+dojo.require("dojo.widget.ContentPane");
+
+dojo.widget.html.DemoEngine = function(){
+	dojo.widget.HtmlWidget.call(this);
+	this.widgetType = "DemoEngine";
+
+	this.templatePath = dojo.uri.dojoUri("src/widget/templates/DemoEngine.html");
+
+	this.demoEngineClass="demoEngine";
+
+	this.navigationNode="";
+	this.navigationClass="demoEngineNavigation";
+
+	this.collapseToNode="";
+	this.collapseToClass="collapseTo";
+
+	this.menuNavigationNode="";
+	this.menuNavigationClass="demoEngineMenuNavigation";
+
+	this.demoNavigationNode="";
+	this.demoNavigationClass="demoEngineDemoNavigation";
+
+	this.demoListWrapperClass="demoListWrapper";
+	this.demoListContainerClass="demoListContainer";
+	this.demoSummaryClass = "demoSummary";
+	this.demoSummaryBoxClass="demoSummaryBox";
+	this.demoListScreenshotClass="screenshot";
+	this.demoListSummaryContainerClass="summaryContainer";
+	this.demoSummaryClass="summary";
+	this.demoViewLinkClass="view";	
+
+	this.demoContainerNode="";
+	this.demoContainerClass="demoEngineDemoContainer";
+
+	this.demoHeaderNode="";
+	this.demoHeaderClass="demoEngineDemoHeader";
+
+	this.collapsedMenuNode="";
+	this.collapsedMenuClass="demoEngineCollapsedMenu";
+	this.collapsedMenuButton="";
+
+	this.aboutNode="";
+	this.aboutClass="demoEngineAbout";
+
+	this.demoPaneNode="";	
+	this.demoTabContainer="";
+
+	this.viewLinkImage="viewDemo.png";
+	this.dojoDemosImage = "dojoDemos.gif";
+	this.dojoDemosImageNode="";
+
+	this.registry = function() {};	
+}
+
+dojo.inherits(dojo.widget.html.DemoEngine, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.html.DemoEngine, {
+	postCreate: function() {
+		dojo.html.addClass(this.domNode, this.demoEngineClass);
+		dojo.html.addClass(this.navigationNode, this.navigationClass);
+		dojo.html.addClass(this.collapseToNode, this.collapseToClass);
+		dojo.html.addClass(this.menuNavigationNode, this.menuNavigationClass);
+		dojo.html.addClass(this.demoNavigationNode, this.demoListWrapperClass);
+		dojo.html.addClass(this.collapsedMenuNode, this.collapsedMenuClass);
+		dojo.html.addClass(this.demoHeaderNode, this.demoHeaderClass);
+		dojo.html.addClass(this.demoContainerNode, this.demoContainerClass);
+
+		// Make sure navigation node is hidden and opaque
+		//dojo.style.hide(this.navigationNode);
+		//dojo.style.setOpacity(this.navigationNode, 0);
+
+		//Make sure demoNavigationNode is hidden and opaque;
+		dojo.style.hide(this.demoNavigationNode);
+		dojo.style.setOpacity(this.demoNavigationNode,0);
+
+		//Make sure demoContainerNode is hidden and opaque
+		dojo.style.hide(this.demoContainerNode);
+		dojo.style.setOpacity(this.demoContainerNode,0);
+
+		//Populate the menu
+		this.buildMenu();
+
+		//show navigationNode
+		//dojo.lfx.html.fadeShow(this.navigationNode, 500).play();
+
+		//turn demoPaneNode into a tabset
+		this.demoTabContainer = dojo.widget.createWidget("TabContainer",{},this.demoPaneNode);	
+
+	},
+
+	buildMenu: function() {
+		dojo.html.removeChildren(this.menuNavigationNode);
+
+		dojo.io.bind({
+			url: "demoRegistry.json",
+			load: dojo.lang.hitch(this, "_buildMenu"),
+			mimetype: "text/json"
+		});
+	},
+
+	_buildMenu: function(type, data) {
+		this.registry = data;
+		dojo.debug("_buildMenu");
+
+		dojo.lang.forEach(this.registry.navigation, dojo.lang.hitch(this,function(category) {
+			this._addMenuItem(category);
+		}));
+	},
+
+	_addMenuItem: function(category) {
+		dojo.debug("Adding button for " + category.name);
+		var newCat = dojo.widget.createWidget("Button");
+		newCat.containerNode.innerHTML=category.name;
+		this.menuNavigationNode.appendChild(newCat.domNode);
+		dojo.event.connect(newCat,"onClick", this, "selectCategory");
+	},
+
+	selectCategory: function(e) {
+		dojo.debug("Selecting: " + e.target.innerHTML);
+		var showDemoNav = dojo.lfx.html.fadeShow(this.demoNavigationNode, 600);
+		var moveMenuNav = dojo.lfx.html.slideTo(this.menuNavigationNode,[0,0], 250);
+
+		dojo.html.removeChildren(this.demoNavigationNode);
+	
+		dojo.lfx.combine(showDemoNav, moveMenuNav).play()	
+
+		for (var x = 0 ; x< this.registry.navigation.length; x++) {
+			if (this.registry.navigation[x].name == e.target.innerHTML) {
+				for (var y=0; y< this.registry.navigation[x].demos.length; y++) {
+					dojo.debug("demo: " + this.registry.navigation[x].demos[y]);
+					var d = this.registry.definitions[this.registry.navigation[x].demos[y]];
+
+					//var demoListWrapper = document.createElement("div");
+					//dojo.html.addClass(demoListWrapper,this.demoListWrapperClass);
+					//this.demoNavigationNode.appendChild(demoListWrapper);
+
+					var demoListContainer=document.createElement("div");
+					dojo.html.addClass(demoListContainer, this.demoListContainerClass);
+					this.demoNavigationNode.appendChild(demoListContainer);
+
+					var demoSummary = document.createElement("div");
+					dojo.html.addClass(demoSummary,this.demoSummaryClass);
+					demoListContainer.appendChild(demoSummary);
+
+					var demoSummaryBox = document.createElement("div");
+					dojo.html.addClass(demoSummaryBox, this.demoSummaryBoxClass);
+					demoSummary.appendChild(demoSummaryBox);
+
+					var table = document.createElement("table");
+					table.width="100%";
+					table.cellSpacing="0";
+					table.cellPadding="0";
+					table.border="0";
+					demoSummaryBox.appendChild(table);
+
+					var tbody = document.createElement("tbody");
+					table.appendChild(tbody);
+
+					var tr= document.createElement("tr");
+					tbody.appendChild(tr);
+
+					var screenshotTd = document.createElement("td");
+					dojo.html.addClass(screenshotTd,this.demoListScreenshotClass);
+					screenshotTd.valign="top";
+					screenshotTd.demoName = this.registry.navigation[x].demos[y];
+					tr.appendChild(screenshotTd);
+
+					var ss = document.createElement("img");
+					ss.src=d.thumbnail;
+					screenshotTd.appendChild(ss);
+
+					var summaryTd = document.createElement("td");
+					dojo.html.addClass(summaryTd,this.demoListSummaryContainerClass);
+					summaryTd.vAlign="top";
+					tr.appendChild(summaryTd);
+
+					var name = document.createElement("h1");
+					name.appendChild(document.createTextNode(this.registry.navigation[x].demos[y]));
+					summaryTd.appendChild(name);
+
+					var summary = document.createElement("div");
+					dojo.html.addClass(summary, this.demoSummaryClass);		
+					summaryTd.appendChild(summary);
+
+					var desc = document.createElement("p");
+					desc.appendChild(document.createTextNode(d.description));
+					summary.appendChild(desc);
+					
+					var viewDiv = document.createElement("div");
+					dojo.html.addClass(viewDiv, this.demoViewLinkClass);
+					summary.appendChild(viewDiv);
+				
+					var viewLink = document.createElement("img");
+					viewLink.src = this.viewLinkImage;
+					viewLink.demoName = this.registry.navigation[x].demos[y];
+					viewDiv.appendChild(viewLink);	
+							
+					dojo.event.connect(viewLink, "onclick", this, "launchDemo");
+					dojo.event.connect(screenshotTd, "onclick", this, "launchDemo");
+				}
+			}
+		}
+	},
+
+	showIframe: function(e) {
+		dojo.lfx.html.fadeShow(e.currentTarget,250).play();
+	},
+
+	launchDemo: function(e) {
+		dojo.debug("Launching Demo: " + e.currentTarget.parentNode.parentNode.parentNode.firstChild.innerHTML);
+		var demo = e.currentTarget.demoName;
+
+		//implode = dojo.lfx.html.implode(this.navigationNode, this.collapsedMenuNode,1500);
+		//show = dojo.lfx.html.fadeShow(this.demoContainerNode,1500);
+		dojo.style.setOpacity(this.demoContainerNode, 0);
+		hide = dojo.lfx.html.fadeHide(this.navigationNode, 500);
+		show = dojo.lfx.html.fadeShow(this.demoContainerNode,500);
+		//dojo.style.setOpacity(this.demoContainerNode, 0);
+		//dojo.style.show(this.demoContainerNode);
+		dojo.lfx.combine(hide,show).play();
+
+		this.demoTabContainer.destroyChildren();
+
+		demoIframe = document.createElement("iframe");
+		demoIframe.src=this.registry.definitions[demo].url;
+
+		dojo.html.removeChildren(this.aboutNode);
+		var name = document.createElement("h1");
+		var about= document.createElement("h2");
+		name.appendChild(document.createTextNode(demo));
+		about.appendChild(document.createTextNode(this.registry.definitions[demo].description));
+		this.aboutNode.appendChild(name);
+		this.aboutNode.appendChild(about);
+
+		liveDemo = dojo.widget.createWidget("ContentPane",{label: "Live Demo"});
+		liveDemo.domNode.appendChild(demoIframe);
+
+		this.demoTabContainer.addChild(liveDemo);
+		demoIframe.parentNode.style.display="inline";
+		demoIframe.parentNode.parentNode.style.overflow="hidden";
+		dojo.io.bind({
+			url: this.registry.definitions[demo].url,
+			mimetype: "text/plain",
+			load: dojo.lang.hitch(this, function(type,data,e) {
+				source = document.createElement("textarea");
+				source.appendChild(document.createTextNode(data));
+				var sourcePane = dojo.widget.createWidget("ContentPane",{label: "Source"});
+				source.rows="20";
+				sourcePane.domNode.appendChild(source);
+				this.demoTabContainer.addChild(sourcePane);
+				dojo.style.show(sourcePane.domNode);
+
+				//let the text area take care of the scrolling 
+				sourcePane.domNode.style.overflow="hidden";
+				
+			})
+		});
+
+		this.demoTabContainer.selectTab(liveDemo);
+	},
+
+	expandDemoNavigation: function(e) {
+		dojo.debug("re expanding navigation");
+		//dojo.style.hide(this.demoContainerNode);
+		//explode = dojo.lfx.html.explode(this.navigationNode,this.collapseToNode,1000);
+		//dojo.style.show(this.navigationNode);
+		//hide = dojo.lfx.html.fadeHide(this.demoContainerNode,250);
+
+		show = dojo.lfx.html.fadeShow(this.navigationNode, 1000);
+		hide = dojo.lfx.html.fadeHide(this.demoContainerNode, 1000);
+		dojo.lfx.combine(show,hide).play();
+	}
+});
+
+dojo.widget.tags.addParseTreeHandler("dojo:DemoEngine");

Added: trunk/PhotoOp/root/static/js/src/widget/html/DocPane.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/html/DocPane.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/html/DocPane.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,210 @@
+/*
+	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.DocPane");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.io.*");
+dojo.require("dojo.event.*");
+dojo.require("dojo.widget.HtmlWidget");
+
+dojo.widget.html.DocPane = function(){
+	dojo.widget.HtmlWidget.call(this);
+
+	this.templatePath = dojo.uri.dojoUri("src/widget/templates/HtmlDocPane.html");
+	this.templateCssPath = dojo.uri.dojoUri("src/widget/templates/HtmlDocPane.css");
+	this.widgetType = "DocPane";
+	this.isContainer = true;
+
+	this.select;
+	this.result;
+	this.fn;
+	this.fnLink;
+	this.count;
+	this.row;
+	this.summary;
+	this.description;
+	this.variables;
+	this.vRow;
+	this.vLink;
+	this.vDesc;
+	this.parameters;
+	this.pRow;
+	this.pLink;
+	this.pDesc;
+	this.pOpt;
+	this.pType;
+	this.source;
+	this.sType;
+	this.sName;
+	this.sParams;
+	this.sPType;
+	this.sPTypeSave;
+	this.sPName;
+	this.sPNameSave;
+
+	dojo.event.topic.subscribe("/doc/results", this, "onDocResults");
+	dojo.event.topic.subscribe("/doc/functionDetail", this, "onDocSelectFunction");
+}
+
+dojo.inherits(dojo.widget.html.DocPane, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.html.DocPane, {
+	fillInTemplate: function(){
+		this.homeSave = this.containerNode.cloneNode(true);
+		this.selectSave = dojo.dom.removeNode(this.select);
+		this.resultSave = dojo.dom.removeNode(this.result);
+		this.rowParent = this.row.parentNode;
+		this.rowSave = dojo.dom.removeNode(this.row);
+		this.vParent = this.vRow.parentNode;
+		this.vSave = dojo.dom.removeNode(this.vRow);
+		this.pParent = this.pRow.parentNode;
+		this.pSave = dojo.dom.removeNode(this.pRow);
+		this.sPTypeSave = dojo.dom.removeNode(this.sPType);
+		this.sPNameSave = dojo.dom.removeNode(this.sPName);
+	},
+
+	onDocSelectFunction: function(message){
+		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;
+
+		var appends = [];
+		dojo.dom.removeChildren(this.domNode);
+		this.fn.innerHTML = message.name;
+		this.description.innerHTML = doc.description;
+
+		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 = "";
+		for(var param in parameters){
+			var paramType = parameters[param][0];
+			var paramName = parameters[param][1];
+			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(doc.parameters[paramName] && doc.parameters[paramName].description){
+				this.pDesc.parentNode.style.display = "inline";
+				this.pDesc.innerHTML = doc.parameters[paramName].description;
+			}
+			appends.push(this.pParent.appendChild(this.pSave.cloneNode(true)));
+			
+			if(param > 0) {
+				this.sParams.appendChild(document.createTextNode(", "));
+			}
+			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;
+		
+		dojo.dom.removeChildren(this.source);
+                this.source.appendChild(document.createTextNode("\n\r"));
+		this.source.appendChild(document.createTextNode(message.src.replace(/\n/g, "\r\n\t")));
+                this.source.appendChild(document.createTextNode("\n\r"));
+		
+		this.domNode.appendChild(this.selectSave.cloneNode(true));
+
+		for(var i = 0, append; append = appends[i]; i++){
+			dojo.dom.removeNode(append);
+		}
+	},
+
+	onDocResults: function(message){
+		var results = message.docResults;
+		
+		if(results.length == 1){
+			dojo.event.topic.publish("/doc/selectFunction", results[0]);
+			return;
+		}
+
+		dojo.dom.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("/doc/selectFunction", 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);
+		}
+	}
+});
+
+dojo.widget.tags.addParseTreeHandler("dojo:DocPane");


Property changes on: trunk/PhotoOp/root/static/js/src/widget/html/DocPane.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/html/DropdownButton.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/html/DropdownButton.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/html/DropdownButton.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,188 @@
+/*
+	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
+*/
+
+/* TODO:
+ * - make the dropdown "smart" so it can't get cutoff on bottom of page, sides of page, etc.
+ */
+
+dojo.provide("dojo.widget.html.DropdownButton");
+
+dojo.require("dojo.event.*");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.HtmlWidget");
+dojo.require("dojo.uri.Uri");
+dojo.require("dojo.dom");
+dojo.require("dojo.style");
+dojo.require("dojo.html");
+
+dojo.widget.html.DropdownButton = function() {
+	// mix in the button properties
+	dojo.widget.DropdownButton.call(this);
+	dojo.widget.HtmlWidget.call(this);
+}
+
+dojo.inherits(dojo.widget.html.DropdownButton, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.html.DropdownButton, {
+	
+	// In IE, event handlers on objects inside buttons don't work correctly, so
+	// we just set onClick on the button itself.
+	templatePath: dojo.uri.dojoUri("src/widget/templates/HtmlDropDownButtonTemplate.html"),
+	templateCssPath: dojo.uri.dojoUri("src/widget/templates/HtmlButtonTemplate.css"),
+
+	// attach points
+	button: null,
+	table: null,
+	labelCell: null,
+	borderCell: null,
+	arrowCell: null,
+	arrow: null,
+
+	fillInTemplate: function(args, frag) {
+		// input data (containing the anchor for the button itself, plus the
+		// thing to display when you push the down arrow)
+		var input = frag["dojo:"+this.widgetType.toLowerCase()]["nodeRef"];
+
+		// Recursively expand widgets inside of the <dojo:dropdownButton>
+		var parser = new dojo.xml.Parse();
+		var frag = parser.parseElement(input, null, true);
+		var ary = dojo.widget.getParser().createComponents(frag);
+
+		this.a = dojo.dom.getFirstChildElement(input);	// the button contents
+		this.menu = dojo.dom.getNextSiblingElement(this.a);	// the menu under the button
+		
+		this.disabled = dojo.html.hasClass(this.a, "disabled");
+		if( this.disabled ) {
+			dojo.html.addClass(this.button, "dojoDisabled");
+			this.domNode.setAttribute("disabled", "true");
+		}
+
+		dojo.html.disableSelection(this.a);
+		this.a.style["text-decoration"]="none";
+		this.labelCell.appendChild(this.a);
+
+		this.arrow.src =
+			dojo.uri.dojoUri("src/widget/templates/images/dropdownButtonsArrow" +
+			(this.disabled ? "-disabled" : "") + ".gif");
+
+		// Attach menu to body so that it appears above other buttons
+		this.menu.style.position="absolute";
+		this.menu.style.display="none";
+		this.menu.style["z-index"] = 99;
+		document.body.appendChild(this.menu);
+	},
+
+	postCreate: function() {
+		if ( dojo.render.html.ie ) {
+			// Compensate for IE's weird padding of button content, which seems to be relative
+			// to the length of the content
+			var contentWidth = dojo.style.getOuterWidth(this.table);
+			this.labelCell.style["left"] = "-" + (contentWidth / 10) + "px";
+			this.arrowCell.style["left"] = (contentWidth / 10) + "px";
+		}
+
+		// Make menu at least as wide as the button
+		var buttonWidth = dojo.style.getOuterWidth(this.button);
+		var menuWidth = dojo.style.getOuterWidth(this.menu);
+		if ( buttonWidth > menuWidth ) {
+			dojo.style.setOuterWidth(this.menu, buttonWidth);
+		}
+	},
+
+	// If someone clicks anywhere else on the screen (including another menu),
+	// then close this menu.
+	onCanvasMouseDown: function(e) {
+		if( !dojo.dom.isDescendantOf(e.target, this.button) &&
+			!dojo.dom.isDescendantOf(e.target, this.menu) ) {
+			this.hideMenu();
+		}
+	},
+
+	eventWasOverArrow: function(e) {
+		// want to use dojo.html.overElement() but also need to detect clicks
+		// on the area between the arrow and the edge of the button
+		var eventX = e.clientX;
+		var borderX = dojo.style.totalOffsetLeft(this.borderCell);
+		return (eventX > borderX );
+	},
+
+	onMouseOver: function(e) {
+		dojo.html.addClass(this.button, "dojoButtonHover");
+		dojo.html.removeClass(this.button, "dojoButtonNoHover");
+	},
+	
+	onMouseOut: function(e) {
+		dojo.html.removeClass(this.button, "dojoButtonHover");
+		dojo.html.addClass(this.button, "dojoButtonNoHover");
+	},
+
+	onClick: function(e) {
+		if ( this.eventWasOverArrow(e) ) {
+			this._onClickArrow();
+		} else {
+			this._onClickButton();
+		}
+	},
+
+	// Action when the user presses the button
+	_onClickButton: function(e) {
+		if ( this.a ) {
+			if ( this.a.click ) {
+				this.a.click();
+			} else if ( this.a.href ) {
+				location.href = this.a.href;
+			}
+		}
+	},
+
+	// Action when user presses the arrow
+	_onClickArrow: function() {
+		if ( this.menu.style.display == "none" ) {
+			this.showMenu();
+		} else {
+			this.hideMenu();
+		}
+	},
+	
+	showMenu: function() {
+		if ( this.disabled )
+			return;
+
+		// Position it accordingly, relative to screen root (since
+		// it's attached to document.body)
+		this.menu.style.left = dojo.style.totalOffsetLeft(this.button) + "px";
+		this.menu.style.top = dojo.style.totalOffsetTop(this.button) + dojo.style.getOuterHeight(this.button) + "px";
+
+		// Display the menu; do this funky code below to stop the menu from extending
+		// all the way to the right edge of the screen.
+		// TODO: retest simple display="" to confirm that it doesn't work.
+		try {
+			this.menu.style.display="table";	// mozilla
+		} catch(e) {
+			this.menu.style.display="block";	// IE
+		}
+
+		// If someone clicks somewhere else on the screen then close the menu
+		dojo.event.connect(document.documentElement, "onmousedown", this, "onCanvasMouseDown");
+		
+		// When someone clicks the menu, after the menu handles the event,
+		// close the menu (be careful not to close the menu too early or else
+		// the menu will never receive the event.)
+		dojo.event.connect(this.menu, "onclick", this, "hideMenu");
+	},
+
+	hideMenu: function() {
+		this.menu.style.display = "none";
+		dojo.event.disconnect(document.documentElement, "onmousedown", this, "onCanvasMouseDown");
+		dojo.event.disconnect(this.menu, "onclick", this, "hideMenu");
+	}
+});
+
+


Property changes on: trunk/PhotoOp/root/static/js/src/widget/html/DropdownButton.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/html/GoogleMap.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/html/GoogleMap.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/html/GoogleMap.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -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.provide("dojo.widget.html.GoogleMap");
+dojo.require("dojo.event.*");
+dojo.require("dojo.html");
+dojo.require("dojo.math");
+dojo.require("dojo.uri.Uri");
+dojo.require("dojo.widget.HtmlWidget");
+dojo.require("dojo.widget.GoogleMap");
+
+(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_){
+		var tag = "<scr"+"ipt src='http://maps.google.com/maps?file=api&amp;v=2&amp;key="+gkey+"'></scri"+"pt>";
+		if(!dj_global["GMap2"]){ // prevent multi-inclusion
+			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 Google in your page or require() the GoogleMap widget before onload has fired");
+	}
+})();
+
+dojo.widget.html.GoogleMap=function(){
+	dojo.widget.HtmlWidget.call(this);
+	dojo.widget.GoogleMap.call(this);
+
+	var gm=dojo.widget.GoogleMap;
+
+	this.map=null;
+	this.data=[];
+	this.datasrc="";
+	// FIXME: this is pehraps the stupidest way to specify this enum I can think of
+	this.controls=[gm.Controls.LargeMap,gm.Controls.Scale,gm.Controls.MapType];
+};
+dojo.inherits(dojo.widget.html.GoogleMap, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.html.GoogleMap, {
+	templatePath:null,
+	templateCssPath:null,
+
+	setControls:function(){
+		var c=dojo.widget.GoogleMap.Controls;
+		for(var i=0; i<this.controls.length; i++){
+			var type=this.controls[i];
+			switch(type){
+				case c.LargeMap:{
+					this.map.addControl(new GLargeMapControl());
+					break;
+				}
+				case c.SmallMap:{
+					this.map.addControl(new GSmallMapControl());
+					break;
+				}
+				case c.SmallZoom:{
+					this.map.addControl(new GSmallZoomControl());
+					break;
+				}
+				case c.Scale:{
+					this.map.addControl(new GScaleControl());
+					break;
+				}
+				case c.MapType:{
+					this.map.addControl(new GMapTypeControl());
+					break;
+				}
+				case c.Overview:{
+					this.map.addControl(new GOverviewMapControl());
+					break;
+				}
+				default:{
+					break;
+				}
+			}
+		}
+	},
+	
+	findCenter:function(bounds){
+		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;
+	},
+
+	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(){
+		var bounds=new GLatLngBounds();
+		var d=this.data;
+		var pts=[];
+		for(var i=0; i<d.length; i++){
+			bounds.extend(new GLatLng(d[i].lat,d[i].lng));
+		}
+
+		this.map.setCenter(this.findCenter(bounds), this.map.getBoundsZoomLevel(bounds));
+
+		for(var i=0; i<this.data.length; i++){
+			var p=new GLatLng(this.data[i].lat,this.data[i].lng);
+			var d=this.data[i].description||null;
+			var m=this.createPinpoint(p,d);
+			this.map.addOverlay(m);
+		}
+	},
+	
+
+	initialize:function(args, frag){
+		if(!GMap2){
+			dojo.raise("dojo.widget.GoogleMap: The Google Map script must be included (with a proper API key) 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]);
+		}
+		this.map=new GMap2(this.domNode);
+		this.render();
+		this.setControls();
+	}
+});


Property changes on: trunk/PhotoOp/root/static/js/src/widget/html/GoogleMap.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/html/LinkPane.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/html/LinkPane.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/html/LinkPane.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,54 @@
+/*
+	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.LinkPane");
+dojo.provide("dojo.widget.html.LinkPane");
+
+//
+// a div that loads from a URL.  (Similar to an iframe, but
+// it's in the same environment as the main window)
+//
+
+dojo.require("dojo.widget.LinkPane");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.event.*");
+dojo.require("dojo.io.*");
+dojo.require("dojo.widget.ContentPane");
+dojo.require("dojo.html");
+dojo.require("dojo.style");
+dojo.require("dojo.dom");
+dojo.require("dojo.string");
+
+
+dojo.widget.html.LinkPane = function(){
+	dojo.widget.html.ContentPane.call(this);
+}
+
+dojo.inherits(dojo.widget.html.LinkPane, dojo.widget.html.ContentPane);
+
+dojo.lang.extend(dojo.widget.html.LinkPane, {
+	widgetType: "LinkPane",
+
+	// 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);
+	}
+});


Property changes on: trunk/PhotoOp/root/static/js/src/widget/html/LinkPane.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/html/Menu.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/html/Menu.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/html/Menu.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,51 @@
+/*
+	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.widget.Menu");
+dojo.provide("dojo.widget.html.Menu");
+
+/* HtmlMenu
+ ***********/
+ 
+dojo.widget.html.Menu = function(){
+	dojo.widget.html.Menu.superclass.constructor.call(this);
+	this.items = [];
+}
+dojo.inherits(dojo.widget.html.Menu, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.html.Menu, {
+	widgetType: "Menu",
+	isContainer: true,
+
+	// copy children widgets output directly to parent (this node), to avoid
+	// errors trying to insert an <li> under a <div>
+	snarfChildDomOutput: true,
+
+	templateString: '<ul></ul>',
+	templateCssPath: dojo.uri.dojoUri("src/widget/templates/Menu.css"),
+	
+	fillInTemplate: function (args, frag){
+		//dojo.widget.HtmlMenu.superclass.fillInTemplate.apply(this, arguments);
+		this.domNode.className = "dojoMenu";
+	},
+	
+ 
+	_register: function (item ) {
+		dojo.event.connect(item, "onSelect", this, "onSelect");
+		this.items.push(item);
+	},
+
+	push: function (item) {
+		this.domNode.appendChild(item.domNode);
+		this._register(item);
+	}
+
+});
+


Property changes on: trunk/PhotoOp/root/static/js/src/widget/html/Menu.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/html/MenuItem.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/html/MenuItem.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/html/MenuItem.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,60 @@
+/*
+	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.MenuItem");
+
+/* HtmlMenuItem
+ ***************/
+
+dojo.widget.html.MenuItem = function(){
+	dojo.widget.HtmlWidget.call(this);
+}
+dojo.inherits(dojo.widget.html.MenuItem, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.html.MenuItem, {
+	widgetType: "MenuItem",
+	templateString: '<li class="dojoMenuItem" dojoAttachEvent="onMouseOver; onMouseOut; onMouseDown; onMouseUp; onClick;"></li>',
+	title: "",
+
+	fillInTemplate: function(args, frag){
+		dojo.html.disableSelection(this.domNode);
+
+		if(!dojo.string.isBlank(this.title)){
+			this.domNode.appendChild(document.createTextNode(this.title));
+		}else{
+			this.domNode.appendChild(frag["dojo:"+this.widgetType.toLowerCase()]["nodeRef"]);
+		}
+	},
+	
+	onMouseOver: function(e){
+		dojo.html.addClass(this.domNode, "dojoMenuItemHover");
+	},
+	
+	onMouseOut: function(e){
+		dojo.html.removeClass(this.domNode, "dojoMenuItemHover");
+	},
+	
+	onClick: function(e){ this.onSelect(this, e); },
+	onMouseDown: function(e){},
+	onMouseUp: function(e){},
+	
+	// By default, when I am clicked, click the item inside of me
+	onSelect: function (item, e) {
+		var child = dojo.dom.getFirstChildElement(this.domNode);
+		if(child){
+			if(child.click){
+				child.click();
+			}else if(child.href){
+				location.href = child.href;
+			}
+		}
+	}
+});
+


Property changes on: trunk/PhotoOp/root/static/js/src/widget/html/MenuItem.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/html/MonthlyCalendar.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/html/MonthlyCalendar.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/html/MonthlyCalendar.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,134 @@
+/*
+	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.MonthlyCalendar");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.html.DatePicker");
+dojo.require("dojo.widget.MonthlyCalendar");
+//dojo.require("dojo.widget.MonthlyCalendar.util");
+dojo.require("dojo.event.*");
+dojo.require("dojo.html");
+
+dojo.widget.html.MonthlyCalendar= function(){
+	dojo.widget.MonthlyCalendar.call(this);
+	//dojo.widget.html.DatePicker.call(this);
+	this.widgetType = "MonthlyCalendar";
+	this.templatePath =  dojo.uri.dojoUri("src/widget/templates/HtmlMonthlyCalendar.html");
+	this.templateCssPath = dojo.uri.dojoUri("src/widget/templates/HtmlMonthlyCalendar.css");
+
+	this.iCalendars = [];
+}
+
+dojo.inherits(dojo.widget.html.MonthlyCalendar, dojo.widget.html.DatePicker);
+
+dojo.lang.extend(dojo.widget.html.MonthlyCalendar, {
+	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.dom.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.style.getContentWidth(node);
+					node.appendChild(el);
+				}
+			}
+		}
+	},
+
+	initUI: function() {
+		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);
+	}	
+});


Property changes on: trunk/PhotoOp/root/static/js/src/widget/html/MonthlyCalendar.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/html/Show.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/html/Show.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/html/Show.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,177 @@
+/*
+	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.Show");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.HtmlWidget");
+dojo.require("dojo.widget.Show");
+dojo.require("dojo.uri.Uri");
+dojo.require("dojo.event");
+dojo.require("dojo.animation.Animation");
+dojo.require("dojo.math.curves");
+dojo.require("dojo.html");
+dojo.require("dojo.lang.common");
+dojo.require("dojo.lang.func");
+
+dojo.widget.defineWidget(
+	"dojo.widget.html.Show",
+	dojo.widget.HtmlWidget,
+	null,
+	"html",
+	function(){
+		dojo.widget.Show.call(this);
+	}
+);
+dojo.lang.extend(dojo.widget.html.Show, dojo.widget.Show.prototype);
+dojo.lang.extend(dojo.widget.html.Show, {
+	body: null,
+	nav: null,
+	hider: null,
+	select: null,
+	option: null,
+	inNav: false,
+	templatePath: dojo.uri.dojoUri("src/widget/templates/HtmlShow.html"),
+	templateCssPath: dojo.uri.dojoUri("src/widget/templates/HtmlShow.css"),
+	fillInTemplate: function(args, frag){
+		var source = this.getFragNodeRef(frag);
+		this.sourceNode = document.body.appendChild(source.cloneNode(true));
+		for(var i = 0, child; child = this.sourceNode.childNodes[i]; i++){
+			if(child.tagName && child.getAttribute("dojotype").toLowerCase() == "showslide"){
+				child.className = "dojoShowPrintSlide";
+				child.innerHTML = "<h1>" + child.title + "</h1>" + child.innerHTML;
+			}
+		}
+		this.sourceNode.className = "dojoShowPrint";
+		this.sourceNode.style.display = "none";
+		
+		dojo.event.connect(document, "onclick", this, "gotoSlideByEvent");
+		dojo.event.connect(document, "onkeypress", this, "gotoSlideByEvent");
+		dojo.event.connect(window, "onresize", this, "resizeWindow");
+		dojo.event.connect(this.nav, "onmousemove", this, "popUpNav");
+	},
+	postCreate: function(){		
+		this._slides = [];
+		for(var i = 0, child; child = this.children[i]; i++){
+			if(child.widgetType == "ShowSlide"){
+				this._slides.push(child);
+				this.option.text = child.title;
+				this.option.parentNode.insertBefore(this.option.cloneNode(true), this.option);
+			}
+		}
+		this.option.parentNode.removeChild(this.option);
+
+		document.body.style.display = "block";
+		this.resizeWindow();
+		this.gotoSlide(0);
+	},
+	gotoSlide: function(/*int*/ slide){
+		if(slide == this._slide){
+			return;
+		}
+
+		if(!this._slides[slide]){
+			// slide: string
+			for(var i = 0, child; child = this._slides[i]; i++){
+				if(child.title == slide){
+					slide = i;
+					break;
+				}
+			}
+		}
+		
+		if(!this._slides[slide]){
+			return;
+		}
+		
+		if(this._slide != -1){
+			while(this._slides[this._slide].previousAction()){}
+		}
+		
+		this._slide = slide;
+		this.select.selectedIndex = slide;
+		while(this.contentNode.hasChildNodes()){ this.contentNode.removeChild(this.contentNode.firstChild); }
+		this.contentNode.appendChild(this._slides[slide].domNode);
+	},
+	gotoSlideByEvent: function(/*Event*/ event){
+		var node = event.target;
+		var type = event.type;
+		if(type == "click"){
+			if(node.tagName == "OPTION"){
+				this.gotoSlide(node.index);
+			}else if(node.tagName == "SELECT"){
+				this.gotoSlide(node.selectedIndex);
+			}else if(node.tagName != "A"){
+				this.nextSlide(event);
+			}
+		}else if(type == "keypress"){
+			var key = event.keyCode;
+			var ch = event.charCode;
+			if(key == 63234 || key == 37){
+				this.previousSlide(event);
+			}else if(key == 63235 || key == 39 || ch == 32){
+				this.nextSlide(event);
+			}
+		}
+	},
+	nextSlide: function(/*Event?*/ event){
+		this.stopEvent(event);
+		return dojo.widget.Show.prototype.nextSlide.call(this, event);
+	},
+	previousSlide: function(/*Event?*/ event){
+		this.stopEvent(event);
+		return dojo.widget.Show.prototype.previousSlide.call(this, event);
+	},
+	stopEvent: function(/*Event*/ ev){
+		if(window.event){
+			ev.returnValue = false;
+			ev.cancelBubble = true;
+		}else{
+			ev.preventDefault();
+			ev.stopPropagation();
+		}
+	},
+	popUpNav: function(){
+		if(!this.inNav){
+			dojo.widget.Show.node = this.nav;
+			var anim = new dojo.animation.Animation(new dojo.math.curves.Line([5], [30]), 250, -1);
+			dojo.event.connect(anim, "onAnimate", function(e) {
+				dojo.widget.Show.node.style.height = e.x + "px";
+			});
+			dojo.event.connect(anim, "onEnd", function(e) {
+				dojo.widget.Show.node.style.height = e.x + "px";
+			});
+			anim.play(true);
+		}
+		clearTimeout(this.inNav);
+		this.inNav = setTimeout(dojo.lang.hitch(this, "hideNav"), 2000);
+	},
+	hideNav: function(){
+		clearTimeout(this.inNav);
+		this.inNav = false;
+
+		dojo.widget.Show.node = this.nav;
+		var anim = new dojo.animation.Animation(new dojo.math.curves.Line([30], [5]), 250, 1);
+		dojo.event.connect(anim, "onAnimate", function(e) {
+			dojo.widget.Show.node.style.height = e.x + "px";
+		});
+		dojo.event.connect(anim, "onEnd", function(e) {
+			dojo.widget.Show.node.style.height = e.x + "px";
+		});
+		anim.play(true);
+	},
+	resizeWindow: function(/*Event*/ ev){
+		document.body.style.height = "auto";
+		var h = Math.max(
+			document.documentElement.scrollHeight || document.body.scrollHeight,
+			dojo.html.getViewportHeight());
+		document.body.style.height = h + "px";
+	}
+});
\ No newline at end of file


Property changes on: trunk/PhotoOp/root/static/js/src/widget/html/Show.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/html/ShowAction.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/html/ShowAction.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/html/ShowAction.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,28 @@
+/*
+	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.ShowAction");
+
+dojo.require("dojo.widget.ShowAction");
+dojo.require("dojo.widget.HtmlWidget");
+dojo.require("dojo.lang.common");
+
+dojo.widget.defineWidget(
+	"dojo.widget.html.ShowAction",
+	dojo.widget.HtmlWidget,
+	null,
+	"html",
+	function(){
+		dojo.widget.ShowAction.call(this);
+	}
+);
+dojo.lang.extend(dojo.widget.html.ShowAction, dojo.widget.ShowAction.prototype);
+dojo.lang.extend(dojo.widget.html.ShowAction, {
+});
\ No newline at end of file


Property changes on: trunk/PhotoOp/root/static/js/src/widget/html/ShowAction.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/html/ShowSlide.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/html/ShowSlide.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/html/ShowSlide.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,189 @@
+/*
+	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.ShowSlide");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.lang.common");
+dojo.require("dojo.widget.ShowSlide");
+dojo.require("dojo.widget.HtmlWidget");
+dojo.require("dojo.lfx.html");
+dojo.require("dojo.animation.Animation");
+dojo.require("dojo.graphics.color");
+
+dojo.widget.defineWidget(
+	"dojo.widget.html.ShowSlide",
+	dojo.widget.HtmlWidget,
+	null,
+	"html",
+	function(){
+		dojo.widget.ShowSlide.call(this);
+	}
+);
+dojo.lang.extend(dojo.widget.html.ShowSlide, dojo.widget.ShowSlide.prototype);
+dojo.lang.extend(dojo.widget.html.ShowSlide, {
+	htmlTitle: null,
+	templatePath: dojo.uri.dojoUri("src/widget/templates/HtmlShowSlide.html"),
+	templateCssPath: dojo.uri.dojoUri("src/widget/templates/HtmlShowSlide.css"),
+	fillInTemplate: function(){
+		this.htmlTitle.innerHTML = this.title;
+
+		this._components = {};
+		var nodes = this.containerNode.all ? this.containerNode.all : this.containerNode.getElementsByTagName('*');
+		for(var i = 0, node; node = nodes[i]; i++){
+			var as = node.getAttribute("as");
+			if(as){
+				if(!this._components[as]){
+					this._components[as] = [];
+				}
+				this._components[as].push(node);
+			}
+		}
+	},
+	postCreate: function(){
+		this._actions = [];
+		for(var i = 0, child; child = this.children[i]; i++){
+			if(child.widgetType == "ShowAction"){
+				this._actions.push(child);
+				var components = this._components[child.on];
+				for(var j = 0, component; component = components[j]; j++){
+					if(child.action && child.action != "remove"){
+						this.hideComponent(component);
+					}
+				}
+			}
+		}
+	},
+	previousAction: function(/*Event?*/ event){
+		this.stopEvent(event);
+
+		var action = this._actions[this._action];
+		if(!action){
+			return false;
+		}
+
+		var on = action.on;
+		while(action.on == on){
+			var components = this._components[on];
+			for(var i = 0, component; component = components[i]; i++){
+				if(action.action == "remove"){
+					if(component.style.display == "none"){
+						component.style.display = "";
+						component.style.visibility = "visible";
+						var exits = true;
+					}
+				}else if(action.action){
+					this.hideComponent(component);
+				}
+			}
+
+			--this._action;
+
+			if(exits){
+				return true;
+			}	
+
+			if(action.auto == "true"){
+				on = this._actions[this._action].on;
+			}
+
+			action = this._actions[this._action];
+			if(!action){
+				return false;
+			}
+		}
+		return true;
+	},
+	hideComponent: function(/*Node*/ component){
+		component.style.visibility = "hidden";
+		component.style.backgroundColor = "transparent";
+		var parent = component.parentNode;
+		if((parent)&&(parent.tagName == "LI")){
+			parent.oldType = parent.style.listStyleType;
+			parent.style.listStyleType = "none";
+		}
+	},
+	nextAction: function(/*Event?*/ event){
+		this.stopEvent(event);
+
+		if(!dojo.widget.ShowSlide.prototype.nextAction.call(this, event)){
+			return false;
+		}
+
+		var action = this._actions[this._action];
+		if(!action){
+			return false;
+		}
+		
+		var components = this._components[action.on];
+		for(var i = 0, component; component = components[i]; i++){
+			if(action.action){
+				var duration = action.duration || 1000;
+				if(action.action == "fade"){
+					dojo.style.setOpacity(component, 0);
+					dojo.lfx.html.fadeIn(component, duration).play(true);
+				}else if(action.action == "fly"){
+					var width = dojo.style.getMarginBoxWidth(component);
+					var position = dojo.style.getAbsolutePosition(component);
+					// alert(position);
+					component.style.position = "relative";
+					component.style.left = -(width + position.x) + "px";
+					dojo.lfx.html.slideBy(component, [0, (width + position.x)], duration, -1, this.callWith).play(true);
+				}else if(action.action == "wipe"){
+					dojo.lfx.html.wipeIn(component, duration).play();
+				}else if(action.action == "color"){
+					var from = new dojo.graphics.color.Color(action.from).toRgb();
+					var to = new dojo.graphics.color.Color(action.to).toRgb();
+					var anim = new dojo.animation.Animation(new dojo.math.curves.Line(from, to), duration, 0);
+					node = component;
+					dojo.event.connect(anim, "onAnimate", function(e) {
+						node.style.color = "rgb(" + e.coordsAsInts().join(",") + ")";
+					});
+					anim.play(true);
+				}else if(action.action == "bgcolor"){
+					dojo.lfx.html.unhighlight(component, action.to, duration).play();
+				}else if(action.action == "remove"){
+					component.style.display = "none";
+				}
+				component.style.visibility = "visible";
+			}
+		}
+		
+		action = this._actions[this._action + 1];
+		if(action && action.auto == "true"){
+			this.nextAction();
+		}
+
+		return true;
+	},
+	callWith: function(/*Node*/ node){
+		if(!node){ return; }
+		if(dojo.lang.isArray(node)){
+			dojo.lang.forEach(node, arguments.callee);
+			return;
+		}
+		var parent = node.parentNode;
+		if((parent)&&(parent.tagName.toLowerCase() == "li")){
+			parent.style.listStyleType = parent.oldType;
+		}
+	},
+	stopEvent: function(/*Event*/ ev){
+		if(!ev){
+			return;
+		}
+		if(window.event){
+			ev.returnValue = false;
+			ev.cancelBubble = true;
+		}else{
+			ev.preventDefault();
+			ev.stopPropagation();
+		}
+	}
+});


Property changes on: trunk/PhotoOp/root/static/js/src/widget/html/ShowSlide.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/html/Slider.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/html/Slider.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/html/Slider.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,500 @@
+/*
+	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
+*/
+
+/**
+ * Slider Widget.
+ * 
+ * The slider widget comes in three forms:
+ *  1. Base Slider widget which supports movement in x and y dimensions
+ *  2. Vertical Slider (SliderVertical) widget which supports movement
+ *     only in the y dimension.
+ *  3. Horizontal Slider (SliderHorizontal) widget which supports movement
+ *     only in the x dimension.
+ *
+ * The key objects in the widget are:
+ *  - a container div which displays a bar in the background (Slider object)
+ *  - a handle inside the container div, which represents the value
+ *    (sliderHandle DOM node)
+ *  - the object which moves the handle (handleMove is of type 
+ *    SliderDragMoveSource)
+ *
+ * The values for the slider are calculated by grouping pixels together, 
+ * based on the number of values to be represented by the slider.
+ * The number of pixels in a group is called the valueSize
+ *  e.g. if slider is 150 pixels long, and is representing the values
+ *       0,1,...10 then pixels are grouped into lots of 15 (valueSize), where:
+ *         value 0 maps to pixels  0 -  7
+ *               1                 8 - 22
+ *               2                23 - 37 etc.
+ * The accuracy of the slider is limited to the number of pixels
+ * (i.e tiles > pixels will result in the slider not being able to
+ *  represent some values).
+ *
+ * Technical Notes:
+ *  - 3 widgets exist because the framework caches the template in
+ *    dojo.widget.fillFromTemplateCache (which ignores the changed URI)
+ *
+ * Todo:
+ *  - Setting of initial value currently doesn't work, because the one or
+ *    more of the offsets, constraints or containing block are not set and
+ *    are required to set the valueSize is not set before fillInTemplate
+ *    is called.
+ *  - Issues with dragging handle when page has been scrolled
+ *  - 
+ *
+ * References (aka sources of inspiration):
+ *  - http://dojotoolkit.org/docs/fast_widget_authoring.html
+ *  - http://dojotoolkit.org/docs/dojo_event_system.html
+ * 
+ * @author Marcel Linnenfelser (m.linnen at synflag.de)
+ * @author Mathew Pole (mathew.pole at ebor.com)
+ *
+ * $Id: $
+ */
+
+// tell the package system what functionality is provided in this module (file)
+// (note that the package system works on modules, not the classes)
+dojo.provide("dojo.widget.html.Slider");
+
+// load dependencies
+dojo.require("dojo.event.*");
+dojo.require("dojo.dnd.*");
+// dojo.dnd.* doesn't include this package, because it's not in __package__.js
+dojo.require("dojo.dnd.HtmlDragMove");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.style");
+
+
+/**
+ * Define the two dimensional slider widget class.
+ */
+dojo.widget.defineWidget (
+	"dojo.widget.html.Slider",
+	dojo.widget.HtmlWidget,
+	{
+		// over-ride some defaults
+		isContainer: false,
+		widgetType: "Slider",
+
+		// useful properties (specified as attributes in the html tag)
+		// number of values to be represented by slider in the horizontal direction
+		valuesX: 10,
+		// number of values to be represented by slider in the vertical direction
+		valuesY: 10,
+		// can values be changed on the x (horizontal) axis?
+		isEnableX: true,
+		// can values be changed on the y (vertical) axis?
+		isEnableY: true,
+		// value size (pixels) in the x dimension
+		valueSizeX: 0.0,
+		// value size (pixels) in the y dimension
+		valueSizeY: 0.0,
+		// initial value in the x dimension
+		initialValueX: 0,
+		// initial value in the y dimension
+		initialValueY: 0,
+
+		// do we allow the user to click on the slider to set the position?
+		// (note: dojo's infrastructor will convert attribute to a boolean)
+		clickSelect: true,
+		// should the handle snap to the grid or remain where it was dragged to?
+		// (note: dojo's infrastructor will convert attribute to a boolean)
+		snapToGrid: false,
+		// should the value change while you are dragging, or just after drag finishes?
+		activeDrag: false,
+
+		templateCssPath: dojo.uri.dojoUri ("src/widget/templates/HtmlSlider.css"),
+		templatePath: dojo.uri.dojoUri ("src/widget/templates/HtmlSlider.html"),
+
+		// our DOM nodes
+		sliderHandle: null,
+
+		// private attributes
+		// This is set to true when a drag is started, so that it is not confused
+		// with a click
+		isDragInProgress: false,
+
+
+		// This function is called when the template is loaded
+		fillInTemplate: function () 
+		{
+			// dojo.debug ("fillInTemplate - className = " + this.domNode.className);
+
+			// setup drag-n-drop for the sliderHandle
+			this.handleMove = new dojo.widget.html.SliderDragMoveSource (this.sliderHandle);
+			this.handleMove.setParent (this);
+			dojo.event.connect(this.handleMove, "onDragMove", this, "onDragMove");
+			dojo.event.connect(this.handleMove, "onDragEnd", this, "onDragEnd");
+			dojo.event.connect(this.handleMove, "onClick", this, "onClick");
+
+			// keep the slider handle inside it's parent container
+			this.handleMove.constrainToContainer = true;
+		
+			if (this.clickSelect) {
+				dojo.event.connect (this.domNode, "onclick", this, "setPosition");
+			} 
+
+			if (this.isEnableX && this.initialValueX > 0) {
+				alert("setting x to " + this.initialValueX);
+				this.setValueX (this.initialValueX);
+			}
+			if (this.isEnableY && this.initialValueY > 0) {
+				this.setValueY (this.initialValueY);
+			}
+		},
+
+
+		// Move the handle (in the x dimension) to the specified value
+		setValueX: function (value) {
+			if (0.0 == this.valueSizeX) {
+				this.valueSizeX = this.handleMove.calcValueSizeX ();
+			}
+			if (value > this.valuesX) {
+				value = this.valuesX;
+			}
+			else if (value < 0) {
+				value = 0;
+			}
+			//dojo.debug ("value = " + value, ", valueSizeX = " + this.valueSizeX);
+			this.handleMove.domNode.style.left = (value * this.valueSizeX) + "px";
+		},
+
+
+		// Get the number of the value that matches the position of the handle
+		getValueX: function () {
+			if (0.0 == this.valueSizeX) {
+				this.valueSizeX = this.handleMove.calcValueSizeX ();
+			}
+			return Math.round (dojo.style.getPixelValue (this.handleMove.domNode, "left") / this.valueSizeX);
+		},
+
+
+		// set the slider to a particular value
+		setValueY: function (value) {
+			if (0.0 == this.valueSizeY) {
+				this.valueSizeY = this.handleMove.calcValueSizeY ();
+			}
+			if (value > this.valuesY) {
+				value = this.valuesY;
+			}
+			else if (value < 0) {
+				value = 0;
+			}
+
+			this.handleMove.domNode.style.top = (value * this.valueSizeY) + "px";
+		},
+
+
+		// Get the number of the value that the matches the position of the handle
+		getValueY: function () {
+			if (0.0 == this.valueSizeY) {
+				this.valueSizeY = this.handleMove.calcValueSizeY ();
+			}
+			return Math.round (dojo.style.getPixelValue (this.handleMove.domNode, "top") / this.valueSizeY);
+		},
+
+
+		// set the position of the handle
+		setPosition: function (e) {
+			//dojo.debug ("Slider#setPosition - e.clientX = " + e.clientX
+			//            + ", e.clientY = " + e.clientY);
+			if (this.isDragInProgress) {
+				this.isDragInProgress = false;
+			}
+
+			var offset = dojo.html.getScrollOffset();
+			var parent = dojo.style.getAbsolutePosition(this.domNode, true);
+			
+			if (this.isEnableX) {
+				var x = offset.x + e.clientX - parent.x;
+				if (x > this.domNode.offsetWidth) {
+					x = this.domNode.offsetWidth;
+				}
+				if (this.snapToGrid && x > 0) {
+					if (0.0 == this.valueSizeX) {
+						this.valueSizeX = this.handleMove.calcValueSizeX ();
+					}
+					x = this.valueSizeX * (Math.round (x / this.valueSizeX));
+				}
+				this.handleMove.domNode.style.left = x + "px";
+			}
+			if (this.isEnableY) {
+				var y = offset.y + e.clientY - parent.y;
+				if (y > this.domNode.offsetHeight) {
+					y = this.domNode.offsetHeight;
+				}
+				if (this.snapToGrid && y > 0) {
+					if (0.0 == this.valueSizeY) {
+						this.valueSizeY = this.handleMove.calcValueSizeY ();
+					}
+					y = this.valueSizeY * (Math.round (y / this.valueSizeY));
+				}
+				this.handleMove.domNode.style.top = y + "px";
+			}
+		},
+
+		onDragMove: function(){
+			this.onValueChanged(this.getValueX(), this.getValueY());
+		},
+	
+		onClick: function(){
+			this.onValueChanged(this.getValueX(), this.getValueY());
+		},
+		
+		onValueChanged: function(x, y){
+		}
+	}
+);
+
+
+/* ------------------------------------------------------------------------- */
+
+
+/**
+ * Define the horizontal slider widget class.
+ */
+dojo.widget.defineWidget (
+	"dojo.widget.html.SliderHorizontal",
+	dojo.widget.html.Slider,
+	{
+		widgetType: "SliderHorizontal",
+
+		value: 0,
+
+		isEnableY: false,
+		templatePath: dojo.uri.dojoUri ("src/widget/templates/HtmlSliderHorizontal.html"),
+
+		postMixInProperties: function(){
+			this.initialValue = this.value;
+		},
+
+		// wrapper for getValueX
+		getValue: function () {
+			return this.getValueX ();
+		},
+
+		// wrapper for setValueX
+		setValue: function (value) {
+			this.setValueX (value);
+			this.onValueChanged(value);
+		},
+
+		onDragMove: function(){
+			if(this.activeDrag){
+				this.onValueChanged(this.getValue());
+			}
+		},
+	
+		onDragEnd: function(){
+			if(!this.activeDrag){
+				this.onValueChanged(this.getValue());
+			}
+		},
+	
+		onClick: function(){
+			this.onValueChanged(this.getValue());
+		},
+		
+		onValueChanged: function(value){
+			this.value=value;
+		}
+	}
+);
+
+
+/* ------------------------------------------------------------------------- */
+
+
+/**
+ * Define the vertical slider widget class.
+ */
+dojo.widget.defineWidget (
+	"dojo.widget.html.SliderVertical",
+	dojo.widget.html.Slider,
+	{
+		widgetType: "SliderVertical",
+
+		value: 0,
+
+		isEnableX: false,
+		templatePath: dojo.uri.dojoUri ("src/widget/templates/HtmlSliderVertical.html"),
+
+		postMixInProperties: function(){
+			this.initialValueY = this.value;
+		},
+
+		// wrapper for getValueY
+		getValue: function () {
+			return this.getValueY ();
+		},
+
+		// wrapper for setValueY
+		setValue: function (value) {
+			this.setValueY (value);
+		},
+
+		onDragMove: function(){
+			if(this.activeDrag){
+				this.onValueChanged(this.getValue());
+			}
+		},
+	
+		onDragEnd: function(){
+			if(!this.activeDrag){
+				this.onValueChanged(this.getValue());
+			}
+		},
+	
+		onClick: function(){
+			this.onValueChanged(this.getValue());
+		},
+		
+		onValueChanged: function(value){
+			this.value=value;
+		}
+	}
+);
+
+
+/* ------------------------------------------------------------------------- */
+
+
+/**
+ * This class extends the HtmlDragMoveSource class to provide
+ * features for the slider handle.
+ */
+dojo.declare (
+	"dojo.widget.html.SliderDragMoveSource",
+	dojo.dnd.HtmlDragMoveSource,
+{
+	isDragInProgress: false,
+	slider: null,
+
+
+	/** Setup the handle for drag
+	 *  Extends dojo.dnd.HtmlDragMoveSource by creating a SliderDragMoveSource */
+	onDragStart: function (e) {
+		this.isDragInProgress = true;
+		this.constrainToContainer = true;
+
+		var dragObj = this.createDragMoveObject ();
+		var constraints = null;
+
+
+		dojo.event.connect (dragObj, "onDragMove", this, "onDragMove");
+
+		return dragObj;
+	},
+
+
+	onDragMove: function (e) {
+		// placeholder to enable event connection
+	},
+
+
+	createDragMoveObject: function () {
+		//dojo.debug ("SliderDragMoveSource#createDragMoveObject - " + this.slider);
+		var dragObj = new dojo.widget.html.SliderDragMoveObject (this.dragObject, this.type);
+		dragObj.slider = this.slider;
+
+		// this code copied from dojo.dnd.HtmlDragSource#onDragStart
+		if (this.dragClass) { 
+			dragObj.dragClass = this.dragClass; 
+		}
+		if (this.constrainToContainer) {
+			dragObj.constrainTo(this.constrainingContainer || this.domNode.parentNode);
+		}
+		return dragObj;
+	},
+
+
+	setParent: function (slider) {
+		this.slider = slider;
+	},
+
+	
+	calcValueSizeX: function () {
+		var dragObj = this.createDragMoveObject ();
+		dragObj.containingBlockPosition = dragObj.domNode.offsetParent ? 
+		dojo.style.getAbsolutePosition(dragObj.domNode.offsetParent) : {x:0, y:0};
+		
+		var constraints = dragObj.getConstraints ();
+		return (constraints.maxX - constraints.minX) / this.slider.valuesX;
+	},
+
+	
+	calcValueSizeY: function () {
+		var dragObj = this.createDragMoveObject ();
+		dragObj.containingBlockPosition = dragObj.domNode.offsetParent ? 
+		dojo.style.getAbsolutePosition(dragObj.domNode.offsetParent) : {x:0, y:0};
+		var constraints = dragObj.getConstraints ();
+		return (constraints.maxY - constraints.minY) / this.slider.valuesY;
+	}
+});
+
+
+/* ------------------------------------------------------------------------- */
+
+
+/**
+ * This class extends the HtmlDragMoveObject class to provide
+ * features for the slider handle.
+ */
+dojo.declare (
+	"dojo.widget.html.SliderDragMoveObject",
+	dojo.dnd.HtmlDragMoveObject,
+{
+	// reference to dojo.widget.html.Slider
+	slider: null,
+
+	/** Moves the node to follow the mouse.
+	 *  Extends functon HtmlDragObject by adding functionality to snap handle
+	 *  to a discrete value */
+	onDragMove: function (e) {
+		if (this.slider.isEnableX && 0.0 == this.slider.valueSizeX) {
+			this.slider.valueSizeX = (this.constraints.maxX - this.constraints.minX) / this.slider.valuesX;
+		}
+		if (this.slider.isEnableY && 0.0 == this.slider.valueSizeY) {
+			this.slider.valueSizeY = (this.constraints.maxY - this.constraints.minY) / this.slider.valuesY;
+		}
+
+		this.updateDragOffset ();
+
+		var x = this.dragOffset.x + e.pageX;
+		var y = this.dragOffset.y + e.pageY;
+
+		if (this.constrainToContainer) {
+			if (x < this.constraints.minX) { x = this.constraints.minX; }
+			if (y < this.constraints.minY) { y = this.constraints.minY; }
+			if (x > this.constraints.maxX) { x = this.constraints.maxX; }
+			if (y > this.constraints.maxY) { y = this.constraints.maxY; }
+		}
+
+		if (this.slider.isEnableX) {
+			var selectedValue = 0;
+			if (x > 0) {
+				selectedValue = Math.round (x / this.slider.valueSizeX);
+			}
+			// dojo.debug ("x = " + x + ", valueSize = " + valueSize 
+			//             + ", selectedValue = " + selectedValue);
+			x = (selectedValue * this.slider.valueSizeX);
+		}
+
+		if (this.slider.isEnableY) {
+			var selectedValue = 0;
+			if (y > 0) {
+				selectedValue = Math.round (y / this.slider.valueSizeY);
+			}
+			y = (selectedValue * this.slider.valueSizeY);
+		}
+
+		this.setAbsolutePosition (x, y);
+	}
+});


Property changes on: trunk/PhotoOp/root/static/js/src/widget/html/Slider.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/html/SortableTable.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/html/SortableTable.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/html/SortableTable.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,582 @@
+/*
+	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.SortableTable");
+dojo.require("dojo.lang");
+dojo.require("dojo.date");
+dojo.require("dojo.html");
+dojo.require("dojo.event.*");
+dojo.require("dojo.widget.HtmlWidget");
+dojo.require("dojo.widget.SortableTable");
+
+dojo.widget.html.SortableTable=function(){
+	//	summary
+	//	Constructor for the SortableTable widget
+	dojo.widget.SortableTable.call(this);
+	dojo.widget.HtmlWidget.call(this);
+
+	this.headClass="";
+	this.tbodyClass="";
+	this.headerClass="";
+	this.headerSortUpClass="selected";
+	this.headerSortDownClass="selected";
+	this.rowClass="";
+	this.rowAlternateClass="alt";
+	this.rowSelectedClass="selected";
+	this.columnSelected="sorted-column";
+};
+dojo.inherits(dojo.widget.html.SortableTable, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.html.SortableTable, {
+	templatePath:null,
+	templateCssPath:null,
+
+	getTypeFromString:function(/* string */ s){
+		//	summary
+		//	Find the constructor that matches param s by searching through the entire object tree.
+		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;	//	function
+	},
+	compare:function(/* object */ o1, /* object */ o2){
+		//	summary
+		//	Compare two objects using a shallow property compare
+		for(var p in o1){
+			if(!(p in o2)) return false;	//	boolean
+			if(o1[p].valueOf()!=o2[p].valueOf()) return false;	//	boolean
+		}
+		return true;	// boolean
+	},
+	isSelected:function(/* object */ o){
+		//	summary
+		//	checked to see if the passed object is in the current selection.
+		for(var i=0;i<this.selected.length;i++){
+			if(this.compare(this.selected[i],o)){
+				return true; // boolean
+			}
+		}
+		return false;	// boolean
+	},
+	removeFromSelected:function(/* object */ o){
+		//	summary
+		//	remove the passed object from the current selection.
+		var idx=-1;
+		for(var i=0;i<this.selected.length;i++){
+			if(this.compare(this.selected[i],o)){
+				idx=i;
+				break;
+			}
+		}
+		if(idx>=0){
+			this.selected.splice(idx,1);
+		}
+	},
+	getSelection:function(){
+		//	summary
+		//	return the array of currently selected objects (JSON format)
+		return this.selected;	//	array
+	},
+	getValue:function(){
+		//	summary
+		//	return a comma-delimited list of selected valueFields.
+		var a=[];
+		for(var i=0;i<this.selected.length;i++){
+			if (this.selected[i][this.valueField]){
+				a.push(this.selected[i][this.valueField]);
+			}
+		}
+		return a.join();	//	string
+	},
+	reset:function(){
+		//	summary
+		//	completely resets the internal representations.
+		this.columns=[];
+		this.data=[];
+		this.resetSelections(this.domNode.getElementsByTagName("tbody")[0]);
+	},
+	resetSelections:function(/* HTMLTableBodyElement */ body){
+		this.selected=[];
+		var idx=0;
+		var rows=body.getElementsByTagName("tr");
+		for(var i=0; i<rows.length; i++){
+			if(rows[i].parentNode==body){
+				rows[i].removeAttribute("selected");
+				if(this.enableAlternateRows&&idx%2==1){
+					rows[i].className=this.rowAlternateClass;
+				}else{
+					rows[i].className="";
+				}
+				idx++;
+			}
+		}
+	},
+
+	getObjectFromRow:function(/* HTMLTableRowElement */ row){
+		//	summary
+		//	creates a JSON object based on the passed row
+		var cells=row.getElementsByTagName("td");
+		var o={};
+		for(var i=0; i<this.columns.length;i++){
+			if(this.columns[i].sortType=="__markup__"){
+				//	FIXME: should we parse this instead?  Because if the user may not get back the markup they put in...
+				o[this.columns[i].getField()]=cells[i].innerHTML;
+			}else{
+				var text=dojo.html.renderedTextContent(cells[i]);
+				var val=new (this.columns[i].getType())(text);
+				o[this.columns[i].getField()]=val;
+			}
+		}
+		if(dojo.html.hasAttribute(row,"value")){
+			o[this.valueField]=dojo.html.getAttribute(row,"value");
+		}
+		return o;	//	object
+	},
+	setSelectionByRow:function(/* HTMLTableElementRow */ row){
+		//	summary
+		//	create the selection object based on the passed row, makes sure it's unique.
+		//	note that you need to call render manually (because of multi-select operations)
+		var o=this.getObjectFromRow(row);
+		var b=false;
+		for(var i=0;i<this.selected.length;i++){
+			if(this.compare(this.selected[i], o)){
+				b=true;
+				break;
+			}
+		}
+		if(!b){
+			this.selected.push(o);
+		}
+	},
+
+	parseColumns:function(/* HTMLTableHeadElement */ node){
+		//	summary
+		//	parses the passed element to create column objects
+		this.reset();
+		var row=node.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={
+				field:null,
+				format:null,
+				noSort:false,
+				sortType:"String",
+				dataType:String,
+				sortFunction:null,
+				label:null,
+				align:"left",
+				valign:"middle",
+				getField:function(){ return this.field||this.label; },
+				getType:function(){ return this.dataType; }
+			};
+			//	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");
+			}
+
+			//	sorting features.
+			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");
+			}
+			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");
+				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]);
+			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")){
+				this.sortIndex=i;
+				var dir=dojo.html.getAttribute(cells[i], "sort");
+				if(!isNaN(parseInt(dir))){
+					dir=parseInt(dir);
+					this.sortDirection=(dir!=0)?1:0;
+				}else{
+					this.sortDirection=(dir.toLowerCase()=="desc")?1:0;
+				}
+			}
+		}
+	},
+
+	parseData:function(/* array */ data){
+		//	summary
+		//	Parse the passed JSON data structure, and cast based on columns.
+		this.data=[];
+		this.selected=[];
+		for(var i=0; i<data.length; i++){
+			var o={};	//	new data object.
+			for(var j=0; j<this.columns.length; j++){
+				var field=this.columns[j].getField();
+				if(this.columns[j].sortType=="__markup__"){
+					o[field]=String(data[i][field]);
+				}else{
+					var type=this.columns[j].getType();
+					var val=data[i][field];
+					var t=this.columns[j].sortType.toLowerCase();
+					if(val){
+						o[field]=new type(val);
+					}else{
+						o[field]=new type();	//	let it use the default.
+					}
+				}
+			}
+			//	check for the valueField if not already parsed.
+			if(data[i][this.valueField]&&!o[this.valueField]){
+				o[this.valueField]=data[i][this.valueField];
+			}
+			this.data.push(o);
+		}
+	}, 
+
+	parseDataFromTable:function(/* HTMLTableBodyElement */ tbody){
+		//	summary
+		//	parses the data in the tbody of a table to create a set of objects.
+		//	Will add objects to this.selected if an attribute 'selected="true"' is present on the row.
+		this.data=[];
+		this.selected=[];
+		var rows=tbody.getElementsByTagName("tr");
+		for(var i=0; i<rows.length; i++){
+			if(dojo.html.getAttribute(rows[i],"ignoreIfParsed")=="true"){
+				continue;
+			}
+			var o={};	//	new data object.
+			var cells=rows[i].getElementsByTagName("td");
+			for(var j=0; j<this.columns.length; j++){
+				var field=this.columns[j].getField();
+				if(this.columns[j].sortType=="__markup__"){
+					//	FIXME: parse this?
+					o[field]=cells[j].innerHTML;
+				}else{
+					var type=this.columns[j].getType();
+					var val=dojo.html.renderedTextContent(cells[j]); //	should be the same index as the column.
+					if (val) o[field]=new type(val);
+					else o[field]=new type();	//	let it use the default.
+				}
+			}
+			if(dojo.html.hasAttribute(rows[i],"value")&&!o[this.valueField]){
+				o[this.valueField]=dojo.html.getAttribute(rows[i],"value");
+			}
+			//	FIXME: add code to preserve row attributes in __metadata__ field?
+			this.data.push(o);
+			
+			//	add it to the selections if selected="true" is present.
+			if(dojo.html.getAttribute(rows[i],"selected")=="true"){
+				this.selected.push(o);
+			}
+		}
+	},
+	
+	showSelections:function(){
+		var body=this.domNode.getElementsByTagName("tbody")[0];
+		var rows=body.getElementsByTagName("tr");
+		var idx=0;
+		for(var i=0; i<rows.length; i++){
+			if(rows[i].parentNode==body){
+				if(dojo.html.getAttribute(rows[i],"selected")=="true"){
+					rows[i].className=this.rowSelectedClass;
+				} else {
+					if(this.enableAlternateRows&&idx%2==1){
+						rows[i].className=this.rowAlternateClass;
+					}else{
+						rows[i].className="";
+					}
+				}
+				idx++;
+			}
+		}
+	},
+	render:function(bDontPreserve){
+		//	summary
+		//	renders the table to the browser
+		var data=[];
+		var body=this.domNode.getElementsByTagName("tbody")[0];
+
+		if(!bDontPreserve){
+			//	rebuild data and selection
+			this.parseDataFromTable(body);
+		}
+
+		//	clone this.data for sorting purposes.
+		for(var i=0; i<this.data.length; i++){
+			data.push(this.data[i]);
+		}
+		
+		var col=this.columns[this.sortIndex];
+		if(!col.noSort){
+			var field=col.getField();
+			if(col.sortFunction){
+				var sort=col.sortFunction;
+			}else{
+				var sort=function(a,b){
+					if (a[field]>b[field]) return 1;
+					if (a[field]<b[field]) return -1;
+					return 0;
+				}
+			}
+			data.sort(sort);
+			if(this.sortDirection!=0) data.reverse();
+		}
+
+		//	build the table and pop it in.
+		while(body.childNodes.length>0) body.removeChild(body.childNodes[0]);
+		for(var i=0; i<data.length;i++){
+			var row=document.createElement("tr");
+			dojo.html.disableSelection(row);
+			if (data[i][this.valueField]){
+				row.setAttribute("value",data[i][this.valueField]);
+			}
+			if(this.isSelected(data[i])){
+				row.className=this.rowSelectedClass;
+				row.setAttribute("selected","true");
+			} else {
+				if(this.enableAlternateRows&&i%2==1){
+					row.className=this.rowAlternateClass;
+				}
+			}
+			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);
+				if(this.sortIndex==j){
+					cell.className=this.columnSelected;
+				}
+				if(this.columns[j].sortType=="__markup__"){
+					cell.innerHTML=data[i][this.columns[j].getField()];
+					for(var k=0; k<cell.childNodes.length; k++){
+						var node=cell.childNodes[k];
+						if(node&&node.nodeType==dojo.html.ELEMENT_NODE){
+							dojo.html.disableSelection(node);
+						}
+					}
+				}else{
+					if(this.columns[j].getType()==Date){
+						var format=this.defaultDateFormat;
+						if(this.columns[j].format) format=this.columns[j].format;
+						cell.appendChild(document.createTextNode(dojo.date.format(data[i][this.columns[j].getField()], format)));
+					}else{
+						cell.appendChild(document.createTextNode(data[i][this.columns[j].getField()]));
+					}
+				}
+				row.appendChild(cell);
+			}
+			body.appendChild(row);
+			dojo.event.connect(row, "onclick", this, "onUISelect");
+		}
+		
+		//	if minRows exist.
+		var minRows=parseInt(this.minRows);
+		if (!isNaN(minRows) && minRows>0 && data.length<minRows){
+			var mod=0;
+			if(data.length%2==0) mod=1;
+			var nRows=minRows-data.length;
+			for(var i=0; i<nRows; i++){
+				var row=document.createElement("tr");
+				row.setAttribute("ignoreIfParsed","true");
+				if(this.enableAlternateRows&&i%2==mod){
+					row.className=this.rowAlternateClass;
+				}
+				for(var j=0;j<this.columns.length;j++){
+					var cell=document.createElement("td");
+					cell.appendChild(document.createTextNode("\u00A0"));
+					row.appendChild(cell);
+				}
+				body.appendChild(row);
+			}
+		}
+	},
+
+	//	the following the user can override.
+	onSelect:function(/* DomEvent */ e){ 
+		//	summary
+		//	empty function for the user to attach code to, fired by onUISelect
+	},
+	onUISelect:function(/* DomEvent */ e){
+		//	summary
+		//	fired when a user selects a row
+		var row=dojo.html.getParentByType(e.target,"tr");
+		var body=dojo.html.getParentByType(row,"tbody");
+		if(this.enableMultipleSelect){
+			if(e.metaKey||e.ctrlKey){
+				if(this.isSelected(this.getObjectFromRow(row))){
+					this.removeFromSelected(this.getObjectFromRow(row));
+					row.removeAttribute("selected");
+				}else{
+					//	push onto the selection stack.
+					this.setSelectionByRow(row);
+					row.setAttribute("selected","true");
+				}
+			}else if(e.shiftKey){
+				//	the tricky one.  We need to figure out the *last* selected row above, 
+				//	and select all the rows in between.
+				var startRow;
+				var rows=body.getElementsByTagName("tr");
+				//	if there's a selection above, we go with that first. 
+				for(var i=0;i<rows.length;i++){
+					if(rows[i].parentNode==body){
+						if(rows[i]==row) break;
+						if(dojo.html.getAttribute(rows[i],"selected")=="true"){
+							startRow=rows[i];
+						}
+					}
+				}
+				//	if there isn't a selection above, we continue with a selection below.
+				if(!startRow){
+					startRow=row;
+					for(;i<rows.length;i++){
+						if(dojo.html.getAttribute(rows[i],"selected")=="true"){
+							row=rows[i];
+							break;
+						}
+					}
+				}
+				this.resetSelections(body);
+				if(startRow==row){
+					//	this is the only selection
+					row.setAttribute("selected","true");
+					this.setSelectionByRow(row);
+				}else{
+					var doSelect=false;
+					for(var i=0; i<rows.length; i++){
+						if(rows[i].parentNode==body){
+							rows[i].removeAttribute("selected");
+							if(rows[i]==startRow){
+								doSelect=true;
+							}
+							if(doSelect){
+								this.setSelectionByRow(rows[i]);
+								rows[i].setAttribute("selected","true");
+							}
+							if(rows[i]==row){
+								doSelect=false;
+							}
+						}
+					}
+				}
+			}else{
+				//	reset the selection
+				this.resetSelections(body);
+				row.setAttribute("selected","true");
+				this.setSelectionByRow(row);
+			}
+		}else{
+			//	reset the data selection and go.
+			this.resetSelections(body);
+			row.setAttribute("selected","true");
+			this.setSelectionByRow(row);
+		}
+		this.showSelections();
+		this.onSelect(e);
+		e.stopPropagation();
+		e.preventDefault();
+	},
+	onHeaderClick:function(/* DomEvent */ e){
+		//	summary
+		//	Main handler function for each header column click.
+		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++){
+			if(headers[i]==header){
+				if(i!=oldIndex){
+					//	new col.
+					this.sortIndex=i;
+					this.sortDirection=0;
+					headers[i].className=this.headerSortDownClass
+				}else{
+					this.sortDirection=(oldDirection==0)?1:0;
+					if(this.sortDirection==0){
+						headers[i].className=this.headerSortDownClass;
+					}else{
+						headers[i].className=this.headerSortUpClass;
+					}
+				}
+			}else{
+				//	reset the header class.
+				headers[i].className=this.headerClass;
+			}
+		}
+		this.render();
+	},
+
+	postCreate:function(){ 
+		// 	summary
+		//	overridden from HtmlWidget, initializes and renders the widget.
+		var thead=this.domNode.getElementsByTagName("thead")[0];
+		if(this.headClass.length>0){
+			thead.className=this.headClass;
+		}
+
+		//	disable selections
+		dojo.html.disableSelection(this.domNode);
+
+		//	parse the columns.
+		this.parseColumns(thead);
+
+		//	attach header handlers.
+		var header="td";
+		if(thead.getElementsByTagName(header).length==0) header="th";
+		var headers=thead.getElementsByTagName(header);
+		for(var i=0; i<headers.length; i++){
+			if(!this.columns[i].noSort){
+				dojo.event.connect(headers[i], "onclick", this, "onHeaderClick");
+			}
+			if(this.sortIndex==i){
+				if(this.sortDirection==0){
+					headers[i].className=this.headerSortDownClass;
+				}else{
+					headers[i].className=this.headerSortUpClass;
+				}
+			}
+		}
+
+		//	parse the tbody element and re-render it.
+		var tbody=this.domNode.getElementsByTagName("tbody")[0];
+		if (this.tbodyClass.length>0) {
+			tbody.className=this.tbodyClass;
+		}
+
+		this.parseDataFromTable(tbody);
+		this.render(true);
+	}
+});


Property changes on: trunk/PhotoOp/root/static/js/src/widget/html/SortableTable.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/html/Spinner.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/html/Spinner.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/html/Spinner.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,383 @@
+/*
+	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.Spinner");
+dojo.require("dojo.widget.Spinner");
+dojo.require("dojo.widget.Manager.*");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.io.*");
+dojo.require("dojo.lfx.*");
+dojo.require("dojo.dom");
+dojo.require("dojo.html");
+dojo.require("dojo.string");
+dojo.require("dojo.widget.html.stabile");
+
+dojo.widget.html.Spinner = function(){
+	dojo.widget.Spinner.call(this);
+	dojo.widget.HtmlWidget.call(this);
+}
+
+dojo.inherits(dojo.widget.html.Spinner, dojo.widget.HtmlWidget);
+
+// copied from superclass since we can't really over-ride via prototype
+dojo.lang.extend(dojo.widget.html.Spinner, dojo.widget.Spinner.defaults);
+
+dojo.lang.extend(dojo.widget.html.Spinner, {
+
+	name: "", // clone in the name from the DOM node
+	inputNode: null,
+	upArrowNode: null,
+	downArrowNode: null,
+	absNode: null,
+	relNode: null,
+	innerRelNode: null,
+	spacerNode: null,
+	inputWidgetId: "",
+	inputWidget: null,
+	typamaticTimer: null,
+	typamaticFunction: null,
+	defaultTimeout: 500,
+	currentTimeout: this.defaultTimeout,
+	eventCount: 0,
+
+	templatePath: dojo.uri.dojoUri("src/widget/templates/HtmlSpinner.html"),
+	templateCssPath: dojo.uri.dojoUri("src/widget/templates/HtmlSpinner.css"),
+
+	setValue: function(value){
+		this.inputWidget.setValue(value);
+		this.inputWidget.adjustValue(0);
+		dojo.widget.html.stabile.setState(this.widgetId, this.getState(), true);
+	},
+
+	getValue: function(){
+		return this.inputWidget.getValue();
+	},
+
+	getState: function(){
+		return {value: this.getValue()};
+	},
+
+	setState: function(state){
+		this.setValue(state.value);
+	},
+
+	// does the keyboard related stuff
+	_handleKeyEvents: function(evt){
+		var k = dojo.event.browser.keys;
+		var keyCode = evt.keyCode;
+
+		switch(keyCode){
+ 			case k.KEY_DOWN_ARROW:
+				dojo.event.browser.stopEvent(evt);
+				this.downArrowPressed(evt);
+				return;
+			case k.KEY_UP_ARROW:
+				dojo.event.browser.stopEvent(evt);
+				this.upArrowPressed(evt);
+				return;
+		}
+		this.eventCount++;
+
+	},
+
+	onKeyDown: function(evt){
+		// IE needs to stop keyDown others need to stop keyPress
+		if(!document.createEvent){ // only IE
+			this._handleKeyEvents(evt);
+		}
+	},
+
+	onKeyPress: function(evt){
+		if(document.createEvent){ // never IE
+			this._handleKeyEvents(evt);
+		}
+	},
+
+	fillInTemplate: function(args, frag){
+		var source = this.getFragNodeRef(frag);
+		dojo.html.copyStyle(this.domNode, source);
+	},
+
+
+	resizeUpArrow: function(){
+		var newh = dojo.style.getContentBoxHeight(this.inputNode) >> 1;
+		if(newh==0){
+			// need more time to calculate size
+			dojo.lang.setTimeout(this, "resizeUpArrow", 100);
+			return;
+		}
+		var oldh = this.upArrowNode.height;
+		if(oldh==0){
+			// need more time to calculate size
+			dojo.lang.setTimeout(this, "resizeUpArrow", 100);
+			return;
+		}
+		var ratio = newh / oldh;
+		this.upArrowNode.width=Math.floor(this.upArrowNode.width * ratio);
+		this.upArrowNode.height=newh;
+	},
+
+	resizeDownArrow: function(){
+		var newh = dojo.style.getContentBoxHeight(this.inputNode) >> 1;
+		if(newh==0){
+			// need more time to calculate size
+			dojo.lang.setTimeout(this, "resizeDownArrow", 100);
+			return;
+		}
+		var oldh = this.downArrowNode.height;
+		if(oldh==0){
+			// need more time to calculate size
+			dojo.lang.setTimeout(this, "resizeDownArrow", 100);
+			return;
+		}
+		var ratio = newh / oldh;
+		this.downArrowNode.width=Math.floor(this.downArrowNode.width * ratio);
+		this.downArrowNode.height=newh;
+	},
+
+	resizeSpacer: function(){
+		var newh = dojo.style.getContentBoxHeight(this.inputNode) >> 1;
+		if( newh==0 ){
+			// need more time to calculate size
+			dojo.lang.setTimeout(this, "resizeSpacer", 100);
+			return;
+		}
+		var oldh = this.downArrowNode.height;
+		if( oldh==0 ){
+			// need more time to calculate size
+			dojo.lang.setTimeout(this, "resizeSpacer", 100);
+			return;
+		}
+		var ratio = newh / oldh;
+		this.spacerNode.width=Math.floor(this.spacerNode.width * ratio);
+		this.spacerNode.height=newh;
+	},
+
+	_pressButton: function(node){
+		with(node.style){
+			borderRight = "0px";
+			borderBottom = "0px";
+			borderLeft = "1px solid black";
+			borderTop = "1px solid black";
+		}
+	},
+
+	_releaseButton: function(node){
+		with(node.style){
+			borderLeft = "0px";
+			borderTop = "0px";
+			borderRight = "1px solid gray";
+			borderBottom = "1px solid gray";
+		}
+	},
+
+	downArrowPressed: function(evt){
+		if(typeof evt != "number"){
+		    if(this.typamaticTimer != null){
+				if(this.typamaticFunction == this.downArrowPressed){
+					return;
+				}
+		        clearTimeout(this.typamaticTimer);
+		    }
+		    this._releaseButton(this.upArrowNode);
+		    this.eventCount++;
+		    this.typamaticTimer = null;
+		    this.currentTimeout = this.defaultTimeout;
+
+		}else if (evt != this.eventCount){
+		    this._releaseButton(this.downArrowNode);
+		    return;
+		}
+		this._pressButton(this.downArrowNode);
+		this.setCursorX(this.inputWidget.adjustValue(-1,this.getCursorX()));
+		this.typamaticFunction = this.downArrowPressed;
+		this.typamaticTimer = setTimeout( dojo.lang.hitch(this,function(){this.downArrowPressed(this.eventCount);}), this.currentTimeout);
+		this.currentTimeout = Math.round(this.currentTimeout * 90 / 100);
+	},
+
+	upArrowPressed: function(evt){
+		if(typeof evt != "number"){
+		    if(this.typamaticTimer != null){
+				if(this.typamaticFunction == this.upArrowPressed){
+					return;
+				}
+		        clearTimeout(this.typamaticTimer);
+		    }
+		    this._releaseButton(this.downArrowNode);
+		    this.eventCount++;
+		    this.typamaticTimer = null;
+		    this.currentTimeout = this.defaultTimeout;
+		}else if(evt != this.eventCount) {
+		    this._releaseButton(this.upArrowNode);
+		    return;
+		}
+		this._pressButton(this.upArrowNode);
+		this.setCursorX(this.inputWidget.adjustValue(+1,this.getCursorX()));
+		this.typamaticFunction = this.upArrowPressed;
+		this.typamaticTimer = setTimeout( dojo.lang.hitch(this,function(){this.upArrowPressed(this.eventCount);}), this.currentTimeout);
+		this.currentTimeout = Math.round(this.currentTimeout * 90 / 100);
+	},
+
+	arrowReleased: function(evt){
+		this.inputNode.focus();
+		if(evt.keyCode && evt.keyCode != null){
+			var keyCode = evt.keyCode;
+			var k = dojo.event.browser.keys;
+
+			switch(keyCode){
+				case k.KEY_DOWN_ARROW:
+				case k.KEY_UP_ARROW:
+					dojo.event.browser.stopEvent(evt);
+					break;
+			}
+		}
+		this._releaseButton(this.upArrowNode);
+		this._releaseButton(this.downArrowNode);
+		this.eventCount++;
+		if(this.typamaticTimer != null){
+		    clearTimeout(this.typamaticTimer);
+		}
+		this.typamaticTimer = null;
+		this.currentTimeout = this.defaultTimeout;
+	},
+
+	mouseWheeled: function(evt) {
+		var scrollAmount = 0;
+		if(typeof evt.wheelDelta == 'number'){ // IE
+		    scrollAmount = evt.wheelDelta;
+		}else if (typeof evt.detail == 'number'){ // Mozilla+Firefox
+		    scrollAmount = -evt.detail;
+		}
+		if(scrollAmount > 0){
+		    this.upArrowPressed(evt);
+		    this.arrowReleased(evt);
+		}else if (scrollAmount < 0){
+		    this.downArrowPressed(evt);
+		    this.arrowReleased(evt);
+		}
+	},
+
+	getCursorX: function(){
+		var x = -1;
+		try{
+		    this.inputNode.focus();
+		    if (typeof this.inputNode.selectionEnd == "number"){
+				x = this.inputNode.selectionEnd;
+		    }else if (document.selection && document.selection.createRange) {
+				var range = document.selection.createRange().duplicate();
+				if(range.parentElement() == this.inputNode){
+					range.moveStart('textedit', -1);
+					x = range.text.length;
+				}
+		    }
+		}catch(e){ /* squelch! */ }
+		return x;
+	},
+
+	setCursorX: function(x){
+		try{
+			this.inputNode.focus();
+		    if(!x){ x = 0 }
+		    if(typeof this.inputNode.selectionEnd == "number"){
+		        this.inputNode.selectionEnd = x;
+		    }else if(this.inputNode.createTextRange){
+		        var range = this.inputNode.createTextRange();
+		        range.collapse(true);
+		        range.moveEnd('character', x);
+		        range.moveStart('character', x);
+		        range.select();
+		    }
+		}catch(e){ /* squelch! */ }
+	},
+
+	postCreate: function(){
+		this.domNode.style.display="none";
+
+		if((typeof this.inputWidgetId != 'string')||(this.inputWidgetId.length == 0)){
+		    var w=dojo.widget.manager.getAllWidgets();
+		    for(var i=w.length-1; i>=0; i--){
+		        if(w[i].adjustValue){
+					this.inputWidget = w[i];
+					break;
+		        }
+		    }
+		}else{
+		    this.inputWidget = dojo.widget.getWidgetById(this.inputWidgetId);
+		}
+
+		if(typeof this.inputWidget != 'object'){
+			dojo.lang.setTimeout(this, "postCreate", 100); 
+			return;
+		}
+		var widgetNode = this.inputWidget.domNode;
+		var inputNodes = widgetNode.getElementsByTagName('INPUT');
+		this.inputNode = inputNodes[0];
+
+		/*
+		// unlink existing dom nodes from domNode
+		this.downArrowNode = dojo.dom.removeNode(this.downArrowNode);
+		this.upArrowNode = dojo.dom.removeNode(this.upArrowNode);
+		this.spacerNode = dojo.dom.removeNode(this.spacerNode);
+		this.innerRelNode = dojo.dom.removeNode(this.innerRelNode);
+		this.absNode = dojo.dom.removeNode(this.absNode);
+		this.relNode = dojo.dom.removeNode(this.relNode);
+		*/
+
+		// create a disconnected node
+		this.innerRelNode.appendChild(this.upArrowNode);
+		this.innerRelNode.appendChild(this.downArrowNode);
+		this.absNode.appendChild(this.innerRelNode);
+		this.relNode.appendChild(this.absNode);
+		this.relNode.appendChild(this.spacerNode);
+
+		dojo.event.connect(this.inputNode, "onkeypress", this, "onKeyPress");
+		dojo.event.connect(this.inputNode, "onkeydown", this, "onKeyDown");
+		dojo.event.connect(this.inputNode, "onkeyup", this, "arrowReleased");
+		dojo.event.connect(this.downArrowNode, "onmousedown", this, "downArrowPressed");
+		dojo.event.connect(this.downArrowNode, "onmouseup", this, "arrowReleased");
+		dojo.event.connect(this.upArrowNode, "onmousedown", this, "upArrowPressed");
+		dojo.event.connect(this.upArrowNode, "onmouseup", this, "arrowReleased");
+		if(this.inputNode.addEventListener){
+			// FIXME: why not use dojo.event.connect() to DOMMouseScroll here?
+		    this.inputNode.addEventListener('DOMMouseScroll', dojo.lang.hitch(this, "mouseWheeled"), false); // Mozilla + Firefox + Netscape
+		}else{
+		    dojo.event.connect(this.inputNode, "onmousewheel", this, "mouseWheeled"); // IE + Safari
+		}
+
+		this.resizeDownArrow();
+		this.resizeUpArrow();
+		this.resizeSpacer();
+
+		// make sure the disconnected node will fit right next to the INPUT tag w/o any interference
+		dojo.html.copyStyle(this.relNode, this.inputNode);
+		with(this.relNode.style){
+			display = "inline";
+			position = "relative";
+			backgroundColor = "";
+			marginLeft = "-1px";
+			paddingLeft = "0";
+		}
+		this.inputNode.style.marginRight = "0px";
+		this.inputNode.style.paddingRight = "0px";
+
+		// add the disconnected node right after the INPUT tag
+		dojo.dom.insertAfter(this.relNode, this.inputNode, false);
+		this.domNode = dojo.dom.removeNode(this.domNode);
+		// realign the spinner vertically in case there's a slight difference
+		var absOffset = dojo.html.getAbsoluteY(this.relNode,true)-dojo.html.getAbsoluteY(this.absNode,true);
+		this.absNode.style.top = absOffset-dojo.style.getBorderExtent(this.inputNode, "top")+"px";
+
+		var s = dojo.widget.html.stabile.getState(this.widgetId);
+		this.setValue(this.getValue());
+		if(s){
+			this.setState(s);
+		}
+	}
+});


Property changes on: trunk/PhotoOp/root/static/js/src/widget/html/Spinner.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/html/TaskBar.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/html/TaskBar.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/html/TaskBar.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,85 @@
+/*
+	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.TaskBar");
+dojo.provide("dojo.widget.html.TaskBarItem");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.FloatingPane");
+dojo.require("dojo.widget.HtmlWidget");
+dojo.require("dojo.event");
+
+// Icon associated w/a floating pane
+dojo.widget.html.TaskBarItem = function(){
+	dojo.widget.TaskBarItem.call(this);
+	dojo.widget.HtmlWidget.call(this);
+}
+dojo.inherits(dojo.widget.html.TaskBarItem, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.html.TaskBarItem, {
+	// constructor arguments
+	iconSrc: '',
+	caption: 'Untitled',
+	window: null,
+	templatePath: dojo.uri.dojoUri("src/widget/templates/HtmlTaskBarItemTemplate.html"),
+	templateCssPath: dojo.uri.dojoUri("src/widget/templates/HtmlTaskBar.css"),
+
+	fillInTemplate: function() {
+		if ( this.iconSrc != '' ) {
+			var img = document.createElement("img");
+			img.src = this.iconSrc;
+			this.domNode.appendChild(img);
+		}
+		this.domNode.appendChild(document.createTextNode(this.caption));
+		dojo.html.disableSelection(this.domNode);
+	},
+
+	postCreate: function() {
+		this.window=dojo.widget.getWidgetById(this.windowId);
+		this.window.explodeSrc = this.domNode;
+		dojo.event.connect(this.window, "destroy", this, "destroy")
+	},
+
+	onClick: function() {
+		this.window.show();
+	}
+});
+
+// Collection of widgets in a bar, like Windows task bar
+dojo.widget.html.TaskBar = function(){
+
+	dojo.widget.html.FloatingPane.call(this);
+	dojo.widget.TaskBar.call(this);
+	this._addChildStack = [];
+}
+
+dojo.inherits(dojo.widget.html.TaskBar, dojo.widget.html.FloatingPane);
+
+dojo.lang.extend(dojo.widget.html.TaskBar, {
+
+	resizable: false,
+	titleBarDisplay: "none",
+
+	addChild: function(child) {
+		if(!this.containerNode){ 
+			this._addChildStack.push(child);
+		}else if(this._addChildStack.length > 0){
+			var oarr = this._addChildStack;
+			this._addChildStack = [];
+			dojo.lang.forEach(oarr, this.addChild, this);
+		}
+		var tbi = dojo.widget.createWidget("TaskBarItem",
+			{	windowId: child.widgetId, 
+				caption: child.title, 
+				iconSrc: child.iconSrc
+			});
+		dojo.widget.html.TaskBar.superclass.addChild.call(this,tbi);
+	}
+});


Property changes on: trunk/PhotoOp/root/static/js/src/widget/html/TaskBar.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/html/TimePicker.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/html/TimePicker.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/html/TimePicker.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,249 @@
+/*
+	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.TimePicker");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.HtmlWidget");
+dojo.require("dojo.widget.TimePicker");
+dojo.require("dojo.event.*");
+dojo.require("dojo.date");
+dojo.require("dojo.html");
+
+dojo.widget.html.TimePicker = function(){
+	dojo.widget.TimePicker.call(this);
+	dojo.widget.HtmlWidget.call(this);
+
+
+	var _this = this;
+	// selected time, JS Date object
+	this.time = "";
+	// set following flag to true if a default time should be set
+	this.useDefaultTime = false;
+	// set the following to true to set default minutes to current time, false to // use zero
+	this.useDefaultMinutes = false;
+	// rfc 3339 date
+	this.storedTime = "";
+	// time currently selected in the UI, stored in hours, minutes, seconds in the format that will be actually displayed
+	this.currentTime = {};
+	this.classNames = {
+		selectedTime: "selectedItem"
+	}
+	this.any = "any"
+	// dom node indecies for selected hour, minute, amPm, and "any time option"
+	this.selectedTime = {
+		hour: "",
+		minute: "",
+		amPm: "",
+		anyTime: false
+	}
+
+	// minutes are ordered as follows: ["12", "6", "1", "7", "2", "8", "3", "9", "4", "10", "5", "11"]
+	this.hourIndexMap = ["", 2, 4, 6, 8, 10, 1, 3, 5, 7, 9, 11, 0];
+	// minutes are ordered as follows: ["00", "30", "05", "35", "10", "40", "15", "45", "20", "50", "25", "55"]
+	this.minuteIndexMap = [0, 2, 4, 6, 8, 10, 1, 3, 5, 7, 9, 11];
+
+	this.templatePath =  dojo.uri.dojoUri("src/widget/templates/HtmlTimePicker.html");
+	this.templateCssPath = dojo.uri.dojoUri("src/widget/templates/HtmlTimePicker.css");
+
+	this.fillInTemplate = function(){
+		this.initData();
+		this.initUI();
+	}
+
+	this.initData = function() {
+		// FIXME: doesn't currently validate the time before trying to set it
+		// Determine the date/time from stored info, or by default don't 
+		//  have a set time
+		// FIXME: should normalize against whitespace on storedTime... for now 
+		// just a lame hack
+		if(this.storedTime.indexOf("T")!=-1 && this.storedTime.split("T")[1] && this.storedTime!=" " && this.storedTime.split("T")[1]!="any") {
+			this.time = dojo.widget.TimePicker.util.fromRfcDateTime(this.storedTime, this.useDefaultMinutes, this.selectedTime.anyTime);
+		} else if (this.useDefaultTime) {
+			this.time = dojo.widget.TimePicker.util.fromRfcDateTime("", this.useDefaultMinutes, this.selectedTime.anyTime);
+		} else {
+			this.selectedTime.anyTime = true;
+			this.time = dojo.widget.TimePicker.util.fromRfcDateTime("", 0, 1);
+		}
+	}
+
+	this.initUI = function() {
+		// set UI to match the currently selected time
+		if(!this.selectedTime.anyTime && this.time) {
+			var amPmHour = dojo.widget.TimePicker.util.toAmPmHour(this.time.getHours());
+			var hour = amPmHour[0];
+			var isAm = amPmHour[1];
+			var minute = this.time.getMinutes();
+			var minuteIndex = parseInt(minute/5);
+			this.onSetSelectedHour(this.hourIndexMap[hour]);
+			this.onSetSelectedMinute(this.minuteIndexMap[minuteIndex]);
+			this.onSetSelectedAmPm(isAm);
+		} else {
+			this.onSetSelectedAnyTime();
+		}
+	}
+
+	this.setDateTime = function(rfcDate) {
+		this.storedTime = rfcDate;
+	}
+	
+	this.onClearSelectedHour = function(evt) {
+		this.clearSelectedHour();
+	}
+
+	this.onClearSelectedMinute = function(evt) {
+		this.clearSelectedMinute();
+	}
+
+	this.onClearSelectedAmPm = function(evt) {
+		this.clearSelectedAmPm();
+	}
+
+	this.onClearSelectedAnyTime = function(evt) {
+		this.clearSelectedAnyTime();
+		if(this.selectedTime.anyTime) {
+			this.selectedTime.anyTime = false;
+			this.time = dojo.widget.TimePicker.util.fromRfcDateTime("", this.useDefaultMinutes);
+			this.initUI();
+		}
+	}
+
+	this.clearSelectedHour = function() {
+		var hourNodes = this.hourContainerNode.getElementsByTagName("td");
+		for (var i=0; i<hourNodes.length; i++) {
+			dojo.html.setClass(hourNodes.item(i), "");
+		}
+	}
+
+	this.clearSelectedMinute = function() {
+		var minuteNodes = this.minuteContainerNode.getElementsByTagName("td");
+		for (var i=0; i<minuteNodes.length; i++) {
+			dojo.html.setClass(minuteNodes.item(i), "");
+		}
+	}
+
+	this.clearSelectedAmPm = function() {
+		var amPmNodes = this.amPmContainerNode.getElementsByTagName("td");
+		for (var i=0; i<amPmNodes.length; i++) {
+			dojo.html.setClass(amPmNodes.item(i), "");
+		}
+	}
+
+	this.clearSelectedAnyTime = function() {
+		dojo.html.setClass(this.anyTimeContainerNode, "anyTimeContainer");
+	}
+
+	this.onSetSelectedHour = function(evt) {
+		this.onClearSelectedAnyTime();
+		this.onClearSelectedHour();
+		this.setSelectedHour(evt);
+		this.onSetTime();
+	}
+
+	this.setSelectedHour = function(evt) {
+		if(evt && evt.target) {
+			dojo.html.setClass(evt.target, this.classNames.selectedTime);
+			this.selectedTime["hour"] = evt.target.innerHTML;
+		} else if (!isNaN(evt)) {
+			var hourNodes = this.hourContainerNode.getElementsByTagName("td");
+			if(hourNodes.item(evt)) {
+				dojo.html.setClass(hourNodes.item(evt), this.classNames.selectedTime);
+				this.selectedTime["hour"] = hourNodes.item(evt).innerHTML;
+			}
+		}
+		this.selectedTime.anyTime = false;
+	}
+
+	this.onSetSelectedMinute = function(evt) {
+		this.onClearSelectedAnyTime();
+		this.onClearSelectedMinute();
+		this.setSelectedMinute(evt);
+		this.selectedTime.anyTime = false;
+		this.onSetTime();
+	}
+
+	this.setSelectedMinute = function(evt) {
+		if(evt && evt.target) {
+			dojo.html.setClass(evt.target, this.classNames.selectedTime);
+			this.selectedTime["minute"] = evt.target.innerHTML;
+		} else if (!isNaN(evt)) {
+			var minuteNodes = this.minuteContainerNode.getElementsByTagName("td");
+			if(minuteNodes.item(evt)) {
+				dojo.html.setClass(minuteNodes.item(evt), this.classNames.selectedTime);
+				this.selectedTime["minute"] = minuteNodes.item(evt).innerHTML;
+			}
+		}
+	}
+
+	this.onSetSelectedAmPm = function(evt) {
+		this.onClearSelectedAnyTime();
+		this.onClearSelectedAmPm();
+		this.setSelectedAmPm(evt);
+		this.selectedTime.anyTime = false;
+		this.onSetTime();
+	}
+
+	this.setSelectedAmPm = function(evt) {
+		if(evt && evt.target) {
+			dojo.html.setClass(evt.target, this.classNames.selectedTime);
+			this.selectedTime["amPm"] = evt.target.innerHTML;
+		} else {
+			evt = evt ? 0 : 1;
+			var amPmNodes = this.amPmContainerNode.getElementsByTagName("td");
+			if(amPmNodes.item(evt)) {
+				dojo.html.setClass(amPmNodes.item(evt), this.classNames.selectedTime);
+				this.selectedTime["amPm"] = amPmNodes.item(evt).innerHTML;
+			}
+		}
+	}
+
+	this.onSetSelectedAnyTime = function(evt) {
+		this.onClearSelectedHour();
+		this.onClearSelectedMinute();
+		this.onClearSelectedAmPm();
+		this.setSelectedAnyTime();
+		this.onSetTime();
+	}
+
+	this.setSelectedAnyTime = function(evt) {
+		this.selectedTime.anyTime = true;
+		dojo.html.setClass(this.anyTimeContainerNode, this.classNames.selectedTime + " " + "anyTimeContainer");
+	}
+
+	this.onClick = function(evt) {
+		dojo.event.browser.stopEvent(evt)
+	}
+
+	this.onSetTime = function() {
+		if(this.selectedTime.anyTime) {
+			this.time = new Date();
+			var tempDateTime = dojo.widget.TimePicker.util.toRfcDateTime(this.time);
+			this.setDateTime(tempDateTime.split("T")[0]);
+		} else {
+			var hour = 12;
+			var minute = 0;
+			var isAm = false;
+			if(this.selectedTime["hour"]) {
+				hour = parseInt(this.selectedTime["hour"], 10);
+			}
+			if(this.selectedTime["minute"]) {
+				minute = parseInt(this.selectedTime["minute"], 10);
+			}
+			if(this.selectedTime["amPm"]) {
+				isAm = (this.selectedTime["amPm"].toLowerCase() == "am");
+			}
+			this.time = new Date();
+			this.time.setHours(dojo.widget.TimePicker.util.fromAmPmHour(hour, isAm));
+			this.time.setMinutes(minute);
+			this.setDateTime(dojo.widget.TimePicker.util.toRfcDateTime(this.time));
+		}
+	}
+
+}
+dojo.inherits(dojo.widget.html.TimePicker, dojo.widget.HtmlWidget);


Property changes on: trunk/PhotoOp/root/static/js/src/widget/html/TimePicker.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/html/TitlePane.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/html/TitlePane.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/html/TitlePane.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,70 @@
+/*
+	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.TitlePane");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.HtmlWidget");
+dojo.require("dojo.lfx.*");
+
+dojo.widget.html.TitlePane = function(){
+	dojo.widget.HtmlWidget.call(this);
+	this.widgetType = "TitlePane";
+
+	this.labelNode="";
+	this.labelNodeClass="";
+	this.containerNodeClass="";
+	this.label="";
+
+	this.open=true;
+	this.templatePath = dojo.uri.dojoUri("src/widget/templates/TitlePane.html");
+}
+
+dojo.inherits(dojo.widget.html.TitlePane, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.html.TitlePane, {
+	isContainer: true,
+	postCreate: function() {
+		if (this.label) {
+			this.labelNode.appendChild(document.createTextNode(this.label));
+		}
+
+		if (this.labelNodeClass) {
+			dojo.html.addClass(this.labelNode, this.labelNodeClass);
+		}	
+
+		if (this.containerNodeClass) {
+			dojo.html.addClass(this.containerNode, this.containerNodeClass);
+		}	
+
+		if (!this.open) {
+			dojo.lfx.wipeOut(this.containerNode,0).play();
+		}
+	},
+
+	onLabelClick: function() {
+		if (this.open) {
+			dojo.lfx.wipeOut(this.containerNode,250).play();
+			this.open=false;
+		}else {
+			dojo.lfx.wipeIn(this.containerNode,250).play();
+			this.open=true;
+		}
+	},
+
+	setContent: function(content) {
+		this.containerNode.innerHTML=content;
+	},
+
+	setLabel: function(label) {
+		this.labelNode.innerHTML=label;
+	}
+});
+
+dojo.widget.tags.addParseTreeHandler("dojo:TitlePane");


Property changes on: trunk/PhotoOp/root/static/js/src/widget/html/TitlePane.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/html/Tooltip.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/html/Tooltip.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/html/Tooltip.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,183 @@
+/*
+	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.Tooltip");
+dojo.require("dojo.widget.html.ContentPane");
+dojo.require("dojo.widget.Tooltip");
+dojo.require("dojo.uri");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.event");
+dojo.require("dojo.style");
+dojo.require("dojo.html");
+
+dojo.widget.defineWidget(
+	"dojo.widget.html.Tooltip",
+	dojo.widget.html.ContentPane,
+	{
+		widgetType: "Tooltip",
+		isContainer: true,
+	
+		// Constructor arguments
+		caption: "",
+		showDelay: 500,
+		hideDelay: 100,
+		connectId: "",
+	
+		templatePath: dojo.uri.dojoUri("src/widget/templates/HtmlTooltipTemplate.html"),
+		templateCssPath: dojo.uri.dojoUri("src/widget/templates/HtmlTooltipTemplate.css"),
+	
+		connectNode: null,
+	
+		// Tooltip has the following possible states:
+		//   erased - nothing on screen
+		//   displaying - currently being faded in (partially displayed)
+		//   displayed - fully displayed
+		//   erasing - currently being faded out (partially erased)
+		state: "erased",
+	
+		fillInTemplate: function(args, frag){
+			if(this.caption != ""){
+				this.domNode.appendChild(document.createTextNode(this.caption));
+			}
+			this.connectNode = dojo.byId(this.connectId);		
+			dojo.widget.html.Tooltip.superclass.fillInTemplate.call(this, args, frag);
+		},
+		
+		postCreate: function(args, frag){
+			// The domnode was appended to my parent widget's domnode, but the positioning
+			// only works if the domnode is a child of document.body
+			document.body.appendChild(this.domNode);
+	
+			dojo.event.connect(this.connectNode, "onmouseover", this, "onMouseOver");
+			dojo.widget.html.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, "show"), 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.state=="displaying"||this.state=="displayed") && !this.hideTimer){
+					this.hideTimer = setTimeout(dojo.lang.hitch(this, "hide"), this.hideDelay);
+				}
+			}
+		},
+	
+		show: function() {
+			if(this.state=="erasing"){
+				// we are in the process of erasing; when that is finished, display it.
+				this.displayScheduled=true;
+				return;
+			}
+			if ( this.state=="displaying" || this.state=="displayed" ) { return; }
+	
+			// prevent IE bleed through (iframe creation is deferred until first show()
+			// call because apparently it takes a long time)
+			if(!this.bgIframe){
+				this.bgIframe = new dojo.html.BackgroundIframe(this.domNode);
+			}
+	
+			this.position();
+	
+			// if rendering using explosion effect, need to set explosion source
+			this.explodeSrc = [this.mouse.x, this.mouse.y];
+	
+			this.state="displaying";
+	
+			dojo.widget.html.Tooltip.superclass.show.call(this);
+		},
+	
+		onShow: function() {
+			dojo.widget.html.Tooltip.superclass.onShow.call(this);
+			
+			this.state="displayed";
+			
+			// in the corner case where the user has moved his mouse away
+			// while the tip was fading in
+			if(this.eraseScheduled){
+				this.hide();
+				this.eraseScheduled=false;
+			}
+		},
+	
+		hide: function() {
+			if(this.state=="displaying"){
+				// in the process of fading in.  wait until that is finished and then fade out
+				this.eraseScheduled=true;
+				return;
+			}
+			if ( this.state=="displayed" ) {
+				this.state="erasing";
+				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.html.Tooltip.superclass.hide.call(this);
+			}
+		},
+	
+		onHide: function(){
+			this.state="erased";
+	
+			// in the corner case where the user has moved his mouse back
+			// while the tip was fading out
+			if(this.displayScheduled){
+				this.display();
+				this.displayScheduled=false;
+			}
+		},
+	
+		position: function(){
+			dojo.html.placeOnScreenPoint(this.domNode, this.mouse.x, this.mouse.y, [10,15], true);
+			this.bgIframe.onResized();
+		},
+	
+		onLoad: function(){
+			if(this.isShowing()){
+				// the tooltip has changed size due to downloaded contents, so reposition it
+				dojo.lang.setTimeout(this, this.position, 50);
+				dojo.widget.html.Tooltip.superclass.onLoad.apply(this, arguments);
+			}
+		},
+	
+		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
+		}
+	}
+);


Property changes on: trunk/PhotoOp/root/static/js/src/widget/html/Tooltip.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/html/YahooMap.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/html/YahooMap.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/html/YahooMap.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,180 @@
+/*
+	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.YahooMap");
+dojo.require("dojo.event.*");
+dojo.require("dojo.html");
+dojo.require("dojo.math");
+dojo.require("dojo.widget.HtmlWidget");
+dojo.require("dojo.widget.YahooMap");
+
+(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 = "<scr"+"ipt src='http://api.maps.yahoo.com/ajaxymap?v=3.0&appid="+yappid+"'></scri"+"pt>";
+		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.html.YahooMap=function(){
+	dojo.widget.HtmlWidget.call(this);
+	dojo.widget.YahooMap.call(this);
+
+	this.map=null;
+	this.datasrc="";
+	this.data=[];
+	this.width=0;
+	this.height=0;
+	this.controls=["zoomlong","maptype","pan"];
+};
+dojo.inherits(dojo.widget.html.YahooMap, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.html.YahooMap, {
+	widgetType: "YahooMap",
+	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<aPts.length; i++){
+			minLat=Math.min(minLat,aPts[i].Lat);
+			maxLat=Math.max(maxLat,aPts[i].Lat);
+			minLon=Math.min(minLon,aPts[i].Lon);
+			maxLon=Math.max(maxLon,aPts[i].Lon);
+		}
+		cLat=dojo.math.round((minLat+maxLat)/2,6);
+		cLon=dojo.math.round((minLon+maxLon)/2,6);
+		return new YGeoPoint(cLat,cLon);
+	},
+	setControls:function(){
+		var c=this.controls;
+		var t=dojo.widget.YahooMap.Controls;
+		for(var i=0; i<c.length; i++){
+			switch(c[i]){
+				case t.MapType:{
+					this.map.addTypeControl();
+					break;
+				}
+				case t.Pan:{
+					this.map.addPanControl();
+					break;
+				}
+				case t.ZoomLong:{
+					this.map.addZoomLong();
+					break;
+				}
+				case t.ZoomShort:{
+					this.map.addZoomShort();
+					break;
+				}
+			}
+		}
+	},
+	
+	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(){
+		var pts=[];
+		var d=this.data;
+		for(var i=0; i<d.length; i++){
+			var pt=new YGeoPoint(d[i].lat, d[i].lng);
+			pts.push(pt);
+			var icon=d[i].icon||null;
+			if(icon){
+				icon=new YImage(icon);
+			}
+			var m=new YMarker(pt,icon);
+			if(d[i].description){
+				m.addAutoExpand("<div>"+d[i].description+"</div>");
+			}
+			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();
+	}
+});


Property changes on: trunk/PhotoOp/root/static/js/src/widget/html/YahooMap.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/html/stabile.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/html/stabile.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/html/stabile.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,213 @@
+/*
+	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
+*/
+
+// Maintain state of widgets when user hits back/forward button
+
+dojo.provide("dojo.widget.html.stabile");
+
+dojo.widget.html.stabile = {
+	// Characters to quote in single-quoted regexprs
+	_sqQuotables: new RegExp("([\\\\'])", "g"),
+
+	// Current depth.
+	_depth: 0,
+
+	// Set to true when calling v.toString, to sniff for infinite
+	// recursion.
+	_recur: false,
+
+	// Levels of nesting of Array and object displays.
+	// If when >= depth, no display or array or object internals.
+	depthLimit: 2
+};
+
+
+
+
+
+//// PUBLIC METHODS
+
+// Get the state stored for the widget with the given ID, or undefined
+// if none.
+// 
+dojo.widget.html.stabile.getState = function(id){
+	dojo.widget.html.stabile.setup();
+	return dojo.widget.html.stabile.widgetState[id];
+}
+
+
+// Set the state stored for the widget with the given ID.  If isCommit
+// is true, commits all widget state to more stable storage.
+// 
+dojo.widget.html.stabile.setState = function(id, state, isCommit){
+	dojo.widget.html.stabile.setup();
+	dojo.widget.html.stabile.widgetState[id] = state;
+	if(isCommit){
+		dojo.widget.html.stabile.commit(dojo.widget.html.stabile.widgetState);
+	}
+}
+
+
+// Sets up widgetState: a hash keyed by widgetId, maps to an object
+// or array writable with "describe".  If there is data in the widget
+// storage area, use it, otherwise initialize an empty object.
+// 
+dojo.widget.html.stabile.setup = function(){
+	if(!dojo.widget.html.stabile.widgetState){
+		var text = dojo.widget.html.stabile.getStorage().value;
+		dojo.widget.html.stabile.widgetState = text ? dj_eval("("+text+")") : {};
+	}
+}
+
+
+// Commits all widget state to more stable storage, so if the user
+// navigates away and returns, it can be restored.
+// 
+dojo.widget.html.stabile.commit = function(state){
+	dojo.widget.html.stabile.getStorage().value = dojo.widget.html.stabile.description(state);
+}
+
+// Return a JSON "description string" for the given value.
+// Supports only core JavaScript types with literals, plus Date,
+// and cyclic structures are unsupported.
+// showAll defaults to false -- if true, this becomes a simple symbolic
+// object dumper, but you cannot "eval" the output.
+//
+dojo.widget.html.stabile.description = function(v, showAll){
+	// Save and later restore dojo.widget.html.stabile._depth;
+	var depth = dojo.widget.html.stabile._depth;
+
+	var describeThis = function() {
+		 return this.description(this, true);
+	} 
+	
+	try {
+
+		if(v===void(0)){
+			return "undefined";
+		}
+		if(v===null){
+			return "null";
+		}
+		if(typeof(v)=="boolean" || typeof(v)=="number"
+		    || v instanceof Boolean || v instanceof Number){
+			return v.toString();
+		}
+
+		if(typeof(v)=="string" || v instanceof String){
+			// Quote strings and their contents as required.
+			// Replacing by $& fails in IE 5.0
+			var v1 = v.replace(dojo.widget.html.stabile._sqQuotables, "\\$1"); 
+			v1 = v1.replace(/\n/g, "\\n");
+			v1 = v1.replace(/\r/g, "\\r");
+			// Any other important special cases?
+			return "'"+v1+"'";
+		}
+
+		if(v instanceof Date){
+			// Create a data constructor.
+			return "new Date("+d.getFullYear+","+d.getMonth()+","+d.getDate()+")";
+		}
+
+		var d;
+		if(v instanceof Array || v.push){
+			// "push" test needed for KHTML/Safari, don't know why -cp
+
+			if(depth>=dojo.widget.html.stabile.depthLimit)
+			  return "[ ... ]";
+
+			d = "[";
+			var first = true;
+			dojo.widget.html.stabile._depth++;
+			for(var i=0; i<v.length; i++){
+				// Skip functions and undefined values
+				// if(v[i]==undef || typeof(v[i])=="function")
+				//   continue;
+				if(first){
+					first = false;
+				}else{
+					d += ",";
+				}
+				d+=arguments.callee(v[i], showAll);
+			}
+			return d+"]";
+		}
+
+		if(v.constructor==Object
+		    || v.toString==describeThis){
+			if(depth>=dojo.widget.html.stabile.depthLimit)
+			  return "{ ... }";
+
+			// Instanceof Hash is good, or if we just use Objects,
+			// we can say v.constructor==Object.
+			// IE (5?) lacks hasOwnProperty, but perhaps objects do not always
+			// have prototypes??
+			if(typeof(v.hasOwnProperty)!="function" && v.prototype){
+				throw new Error("description: "+v+" not supported by script engine");
+			}
+			var first = true;
+			d = "{";
+			dojo.widget.html.stabile._depth++;
+			for(var key in v){
+				// Skip values that are functions or undefined.
+				if(v[key]==void(0) || typeof(v[key])=="function")
+					continue;
+				if(first){
+					first = false;
+				}else{
+					d += ", ";
+				}
+				var kd = key;
+				// If the key is not a legal identifier, use its description.
+				// For strings this will quote the stirng.
+				if(!kd.match(/^[a-zA-Z_][a-zA-Z0-9_]*$/)){
+					kd = arguments.callee(key, showAll);
+				}
+				d += kd+": "+arguments.callee(v[key], showAll);
+			}
+			return d+"}";
+		}
+
+		if(showAll){
+			if(dojo.widget.html.stabile._recur){
+				// Save the original definitions of toString;
+				var objectToString = Object.prototype.toString;
+				return objectToString.apply(v, []);
+			}else{
+				dojo.widget.html.stabile._recur = true;
+				return v.toString();
+			}
+		}else{
+			// log("Description? "+v.toString()+", "+typeof(v));
+			throw new Error("Unknown type: "+v);
+			return "'unknown'";
+		}
+
+	} finally {
+		// Always restore the global current depth.
+		dojo.widget.html.stabile._depth = depth;
+	}
+
+}
+
+
+
+//// PRIVATE TO MODULE
+
+// Gets an object (form field) with a read/write "value" property.
+// 
+dojo.widget.html.stabile.getStorage = function(){
+	if (dojo.widget.html.stabile.dataField) {
+		return dojo.widget.html.stabile.dataField;
+	}
+	var form = document.forms._dojo_form;
+	return dojo.widget.html.stabile.dataField = form ? form.stabile : {value: ""};
+}
+


Property changes on: trunk/PhotoOp/root/static/js/src/widget/html/stabile.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/svg/Chart.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/svg/Chart.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/svg/Chart.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,531 @@
+/*
+	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.svg.Chart");
+
+dojo.require("dojo.widget.HtmlWidget");
+dojo.require("dojo.widget.Chart");
+dojo.require("dojo.math");
+dojo.require("dojo.html");
+dojo.require("dojo.svg");
+dojo.require("dojo.graphics.color");
+
+dojo.widget.svg.Chart=function(){
+	dojo.widget.Chart.call(this);
+	dojo.widget.HtmlWidget.call(this);
+};
+dojo.inherits(dojo.widget.svg.Chart, dojo.widget.HtmlWidget);
+dojo.lang.extend(dojo.widget.svg.Chart, {
+	//	widget props
+	templatePath:null,
+	templateCssPath:null,
+
+	//	state
+	_isInitialized:false,
+	hasData:false,
+
+	//	chart props
+	vectorNode:null,
+	plotArea:null,
+	dataGroup:null,
+	axisGroup:null,
+
+	properties:{
+		height:400,	//	defaults, will resize to the domNode.
+		width:600,
+		plotType:null,
+		padding:{
+			top:10,
+			bottom:2,
+			left:60,
+			right:30
+		},
+		axes:{
+			x:{
+				plotAt:0,
+				label:"",
+				unitLabel:"",
+				unitType:Number,
+				nUnitsToShow:10,
+				range:{
+					min:0,
+					max:200
+				}
+			},
+			y:{
+				plotAt:0,
+				label:"",
+				unitLabel:"",
+				unitType:Number,
+				nUnitsToShow:10,
+				range:{
+					min:0,
+					max:200
+				}
+			}
+		}
+	},
+	
+	fillInTemplate:function(args,frag){
+		this.initialize();
+		this.render();
+	},
+	parseData:function(){
+	},
+	initialize:function(){
+		this.parseData();
+	
+		//	begin by grabbing the table, and reading it in.
+		var table=this.domNode.getElementsByTagName("table")[0];
+		if (!table) return;
+		
+		var bRangeX=false;
+		var bRangeY=false;
+		
+		//	properties off the table
+		if (table.getAttribute("width")) this.properties.width=table.getAttribute("width");
+		if (table.getAttribute("height")) this.properties.height=table.getAttribute("height");
+		if (table.getAttribute("plotType")) this.properties.plotType=table.getAttribute("plotType");
+		if (table.getAttribute("padding")){
+			if (table.getAttribute("padding").indexOf(",") > -1)
+				var p=table.getAttribute("padding").split(","); 
+			else var p=table.getAttribute("padding").split(" ");
+			if (p.length==1){
+				var pad=parseFloat(p[0]);
+				this.properties.padding.top=pad;
+				this.properties.padding.right=pad;
+				this.properties.padding.bottom=pad;
+				this.properties.padding.left=pad;
+			} else if(p.length==2){
+				var padV=parseFloat(p[0]);
+				var padH=parseFloat(p[1]);
+				this.properties.padding.top=padV;
+				this.properties.padding.right=padH;
+				this.properties.padding.bottom=padV;
+				this.properties.padding.left=padH;
+			} else if(p.length==4){
+				this.properties.padding.top=parseFloat(p[0]);
+				this.properties.padding.right=parseFloat(p[1]);
+				this.properties.padding.bottom=parseFloat(p[2]);
+				this.properties.padding.left=parseFloat(p[3]);
+			}
+		}
+		if (table.getAttribute("rangeX")){
+			var p=table.getAttribute("rangeX");
+			if (p.indexOf(",")>-1) p=p.split(",");
+			else p=p.split(" ");
+			this.properties.axes.x.range.min=parseFloat(p[0]);
+			this.properties.axes.x.range.max=parseFloat(p[1]);
+			bRangeX=true;
+		}
+		if (table.getAttribute("rangeY")){
+			var p=table.getAttribute("rangeY");
+			if (p.indexOf(",")>-1) p=p.split(",");
+			else p=p.split(" ");
+			this.properties.axes.y.range.min=parseFloat(p[0]);
+			this.properties.axes.y.range.max=parseFloat(p[1]);
+			bRangeY=true;
+		}
+
+		var thead=table.getElementsByTagName("thead")[0];
+		var tbody=table.getElementsByTagName("tbody")[0];
+		if(!(thead&&tbody)) dojo.raise("dojo.widget.Chart: supplied table must define a head and a body.");
+
+		//	set up the series.
+		var columns=thead.getElementsByTagName("tr")[0].getElementsByTagName("th");	//	should be <tr><..>
+		
+		//	assume column 0 == X
+		for (var i=1; i<columns.length; i++){
+			var key="column"+i;
+			var label=columns[i].innerHTML;
+			var plotType=columns[i].getAttribute("plotType")||"line";
+			var color=columns[i].getAttribute("color");
+			var ds=new dojo.widget.Chart.DataSeries(key,label,plotType,color);
+			this.series.push(ds);
+		}
+
+		//	ok, get the values.
+		var rows=tbody.getElementsByTagName("tr");
+		var xMin=Number.MAX_VALUE,xMax=Number.MIN_VALUE;
+		var yMin=Number.MAX_VALUE,yMax=Number.MIN_VALUE;
+		var ignore = [
+			"accesskey","align","bgcolor","class",
+			"colspan","height","id","nowrap",
+			"rowspan","style","tabindex","title",
+			"valign","width"
+		];
+
+		for(var i=0; i<rows.length; i++){
+			var row=rows[i];
+			var cells=row.getElementsByTagName("td");
+			var x=Number.MIN_VALUE;
+			for (var j=0; j<cells.length; j++){
+				if (j==0){
+					x=parseFloat(cells[j].innerHTML);
+					xMin=Math.min(xMin, x);
+					xMax=Math.max(xMax, x);
+				} else {
+					var ds=this.series[j-1];
+					var y=parseFloat(cells[j].innerHTML);
+					yMin=Math.min(yMin,y);
+					yMax=Math.max(yMax,y);
+					var o={x:x, value:y};
+					var attrs=cells[j].attributes;
+					for(var k=0; k<attrs.length; k++){
+						var attr=attrs.item(k);
+						var bIgnore=false;
+						for (var l=0; l<ignore.length; l++){
+							if (attr.nodeName.toLowerCase()==ignore[l]){
+								bIgnore=true;
+								break;
+							}
+						}
+						if(!bIgnore) o[attr.nodeName]=attr.nodeValue;
+					}
+					ds.add(o);
+				}
+			}
+		}
+
+		//	fix the axes
+		if(!bRangeX){
+			this.properties.axes.x.range={min:xMin, max:xMax};
+		}
+		if(!bRangeY){
+			this.properties.axes.y.range={min:yMin, max:yMax};
+		}
+
+		//	where to plot the axes
+		if (table.getAttribute("axisAt")){
+			var p=table.getAttribute("axisAt");
+			if (p.indexOf(",")>-1) p=p.split(",");
+			else p=p.split(" ");
+			
+			//	x axis
+			if (!isNaN(parseFloat(p[0]))){
+				this.properties.axes.x.plotAt=parseFloat(p[0]);
+			} else if (p[0].toLowerCase()=="ymin"){
+				this.properties.axes.x.plotAt=this.properties.axes.y.range.min;
+			} else if (p[0].toLowerCase()=="ymax"){
+				this.properties.axes.x.plotAt=this.properties.axes.y.range.max;
+			}
+
+			// y axis
+			if (!isNaN(parseFloat(p[1]))){
+				this.properties.axes.y.plotAt=parseFloat(p[1]);
+			} else if (p[1].toLowerCase()=="xmin"){
+				this.properties.axes.y.plotAt=this.properties.axes.x.range.min;
+			} else if (p[1].toLowerCase()=="xmax"){
+				this.properties.axes.y.plotAt=this.properties.axes.x.range.max;
+			}
+		} else {
+			this.properties.axes.x.plotAt=this.properties.axes.y.range.min;
+			this.properties.axes.y.plotAt=this.properties.axes.x.range.min;
+		}
+
+		//	table values should be populated, now pop it off.
+		this.domNode.removeChild(table);
+
+		//	get the width and the height.
+//		this.properties.width=dojo.html.getInnerWidth(this.domNode);
+//		this.properties.height=dojo.html.getInnerHeight(this.domNode);
+
+		// ok, lets create the chart itself.
+		dojo.svg.g.suspend();		
+		if(this.vectorNode) this.destroy();
+		this.vectorNode=document.createElementNS(dojo.svg.xmlns.svg, "svg");
+		this.vectorNode.setAttribute("width", this.properties.width);
+		this.vectorNode.setAttribute("height", this.properties.height);
+
+		//	set up the clip path for the plot area.
+		var defs = document.createElementNS(dojo.svg.xmlns.svg, "defs");
+		var clip = document.createElementNS(dojo.svg.xmlns.svg, "clipPath");
+		clip.setAttribute("id","plotClip"+this.widgetId);
+		var rect = document.createElementNS(dojo.svg.xmlns.svg, "rect");		
+		rect.setAttribute("x", this.properties.padding.left);
+		rect.setAttribute("y", this.properties.padding.top);
+		rect.setAttribute("width", this.properties.width-this.properties.padding.left-this.properties.padding.right);
+		rect.setAttribute("height", this.properties.height-this.properties.padding.bottom-this.properties.padding.bottom);
+		clip.appendChild(rect);
+		defs.appendChild(clip);
+		this.vectorNode.appendChild(defs);
+
+		//	the plot background.
+		this.plotArea = document.createElementNS(dojo.svg.xmlns.svg, "g");
+		this.vectorNode.appendChild(this.plotArea);
+		var rect = document.createElementNS(dojo.svg.xmlns.svg, "rect");		
+		rect.setAttribute("x", this.properties.padding.left);
+		rect.setAttribute("y", this.properties.padding.top);
+		rect.setAttribute("width", this.properties.width-this.properties.padding.left-this.properties.padding.right);
+		rect.setAttribute("height", this.properties.height-this.properties.padding.bottom-this.properties.padding.bottom);
+		rect.setAttribute("fill", "#fff");
+		this.plotArea.appendChild(rect);
+
+		//	data group
+		this.dataGroup = document.createElementNS(dojo.svg.xmlns.svg, "g");
+		this.dataGroup.setAttribute("style","clip-path:url(#plotClip"+this.widgetId+");");
+		this.plotArea.appendChild(this.dataGroup);
+
+		//	axis group
+		this.axisGroup = document.createElementNS(dojo.svg.xmlns.svg, "g");
+		this.plotArea.appendChild(this.axisGroup);
+
+		//	x axis
+		var stroke=1;
+		var line = document.createElementNS(dojo.svg.xmlns.svg, "line");
+		var y=dojo.widget.svg.Chart.Plotter.getY(this.properties.axes.x.plotAt, this);
+		line.setAttribute("y1", y);
+		line.setAttribute("y2", y);
+		line.setAttribute("x1",this.properties.padding.left-stroke);
+		line.setAttribute("x2",this.properties.width-this.properties.padding.right);
+		line.setAttribute("style","stroke:#000;stroke-width:"+stroke+";");
+		this.axisGroup.appendChild(line);
+		
+		//	x axis units.
+		//	(min and max)
+		var textSize=10;
+		var text = document.createElementNS(dojo.svg.xmlns.svg, "text");
+		text.setAttribute("x", this.properties.padding.left);
+		text.setAttribute("y", this.properties.height-this.properties.padding.bottom+textSize+2);
+		text.setAttribute("style", "text-anchor:middle;font-size:"+textSize+"px;fill:#000;");
+		text.appendChild(document.createTextNode(dojo.math.round(parseFloat(this.properties.axes.x.range.min),2)));
+		this.axisGroup.appendChild(text);
+		
+		var text = document.createElementNS(dojo.svg.xmlns.svg, "text");
+		text.setAttribute("x", this.properties.width-this.properties.padding.right-(textSize/2));
+		text.setAttribute("y", this.properties.height-this.properties.padding.bottom+textSize+2);
+		text.setAttribute("style", "text-anchor:middle;font-size:"+textSize+"px;fill:#000;");
+		text.appendChild(document.createTextNode(dojo.math.round(parseFloat(this.properties.axes.x.range.max),2)));
+		this.axisGroup.appendChild(text);	
+		
+		//	y axis
+		var line=document.createElementNS(dojo.svg.xmlns.svg, "line");
+		var x=dojo.widget.svg.Chart.Plotter.getX(this.properties.axes.y.plotAt, this);
+		line.setAttribute("x1", x);
+		line.setAttribute("x2", x);
+		line.setAttribute("y1", this.properties.padding.top);
+		line.setAttribute("y2", this.properties.height-this.properties.padding.bottom);
+		line.setAttribute("style", "stroke:#000;stroke-width:"+stroke+";");
+		this.axisGroup.appendChild(line);
+
+		//	y axis units
+		var text = document.createElementNS(dojo.svg.xmlns.svg, "text");
+		text.setAttribute("x", this.properties.padding.left-4);
+		text.setAttribute("y", this.properties.height-this.properties.padding.bottom);
+		text.setAttribute("style", "text-anchor:end;font-size:"+textSize+"px;fill:#000;");
+		text.appendChild(document.createTextNode(dojo.math.round(parseFloat(this.properties.axes.y.range.min),2)));
+		this.axisGroup.appendChild(text);
+		
+		var text = document.createElementNS(dojo.svg.xmlns.svg, "text");
+		text.setAttribute("x", this.properties.padding.left-4);
+		text.setAttribute("y", this.properties.padding.top+(textSize/2));
+		text.setAttribute("style", "text-anchor:end;font-size:"+textSize+"px;fill:#000;");
+		text.appendChild(document.createTextNode(dojo.math.round(parseFloat(this.properties.axes.y.range.max),2)));
+		this.axisGroup.appendChild(text);	
+
+		this.domNode.appendChild(this.vectorNode);
+		dojo.svg.g.resume();
+
+		//	this is last.
+		this.assignColors();
+		this._isInitialized=true;
+	},
+	destroy:function(){
+		while(this.domNode.childNodes.length>0){
+			this.domNode.removeChild(this.domNode.childNodes.item(0));
+		}
+		this.vectorNode=this.plotArea=this.dataGroup=this.axisGroup=null;
+	},
+	render:function(){
+		dojo.svg.g.suspend();
+		
+		if (this.dataGroup){
+			while(this.dataGroup.childNodes.length>0){
+				this.dataGroup.removeChild(this.dataGroup.childNodes.item(0));
+			}
+		} else {
+			this.initialize();
+		}
+
+		//	the remove/append is an attempt to streamline the rendering, it's totally optional
+//		var p=this.dataGroup.parentNode;
+//		p.removeChild(this.dataGroup);
+		for(var i=0; i<this.series.length; i++){
+			dojo.widget.svg.Chart.Plotter.plot(this.series[i], this);
+		}
+//		p.appendChild(this.dataGroup);
+		
+		dojo.svg.g.resume();
+	}
+});
+
+dojo.widget.svg.Chart.Plotter=new function(){
+	var _this=this;
+	var plotters = {};
+	var types=dojo.widget.Chart.PlotTypes;
+	
+	this.getX=function(value, chart){
+		var v=parseFloat(value);
+		var min=chart.properties.axes.x.range.min;
+		var max=chart.properties.axes.x.range.max;
+		var ofst=0-min;
+		min+=ofst; max+=ofst; v+=ofst;
+
+		var xmin=chart.properties.padding.left;
+		var xmax=chart.properties.width-chart.properties.padding.right;
+		var x=(v*((xmax-xmin)/max))+xmin;
+		return x;
+	};
+	this.getY=function(value, chart){
+		var v=parseFloat(value);
+		var max=chart.properties.axes.y.range.max;
+		var min=chart.properties.axes.y.range.min;
+		var ofst=0;
+		if(min<0)ofst+=Math.abs(min);
+		min+=ofst; max+=ofst; v+=ofst;
+		
+		var ymin=chart.properties.height-chart.properties.padding.bottom;
+		var ymax=chart.properties.padding.top;
+		var y=(((ymin-ymax)/(max-min))*(max-v))+ymax;
+		return y;
+	};
+
+	this.addPlotter=function(name, func){
+		plotters[name]=func;
+	};
+	this.plot=function(series, chart){
+		if (series.values.length==0) return;
+		if (series.plotType && plotters[series.plotType]){
+			return plotters[series.plotType](series, chart);
+		}
+		else if (chart.plotType && plotters[chart.plotType]){
+			return plotters[chart.plotType](series, chart);
+		}
+	};
+
+	//	plotting
+	plotters[types.Bar]=function(series, chart){
+		var space=1;
+		var lastW = 0;
+		for (var i=0; i<series.values.length; i++){
+			var x=_this.getX(series.values[i].x, chart);
+			var w;
+			if (i==series.values.length-1){
+				w=lastW;
+			} else{
+				w=_this.getX(series.values[i+1].x, chart)-x-space;
+				lastW=w;
+			}
+			x-=(w/2);
+
+			var yA=_this.getY(chart.properties.axes.x.plotAt, chart);
+			var y=_this.getY(series.values[i].value, chart);
+			var h=Math.abs(yA-y);
+			if (parseFloat(series.values[i].value)<chart.properties.axes.x.plotAt){
+				var oy=yA;
+				yA=y;
+				y=oy;
+			}
+
+			var bar=document.createElementNS(dojo.svg.xmlns.svg, "rect");
+			bar.setAttribute("fill", series.color);
+			bar.setAttribute("title", series.label + ": " + series.values[i].value);
+			bar.setAttribute("stroke-width", "0");
+			bar.setAttribute("x", x);
+			bar.setAttribute("y", y);
+			bar.setAttribute("width", w);
+			bar.setAttribute("height", h);
+			bar.setAttribute("fill-opacity", "0.9");
+			chart.dataGroup.appendChild(bar);
+		}
+	};
+	plotters[types.Line]=function(series, chart){
+		var tension=3;
+		var line = document.createElementNS(dojo.svg.xmlns.svg, "path");
+		line.setAttribute("fill", "none");
+		line.setAttribute("stroke", series.color);
+		line.setAttribute("stroke-width", "2");
+		line.setAttribute("stroke-opacity", "0.85");
+		line.setAttribute("title", series.label);
+		chart.dataGroup.appendChild(line);
+
+		var path = [];
+		for (var i=0; i<series.values.length; i++){
+			var x = _this.getX(series.values[i].x, chart)
+			var y = _this.getY(series.values[i].value, chart);
+
+			var dx = chart.properties.padding.left+1;
+			var dy = chart.properties.height-chart.properties.padding.bottom;
+			if (i>0){
+				dx=x-_this.getX(series.values[i-1].x, chart);
+				dy=_this.getY(series.values[i-1].value, chart);
+			}
+			
+			if (i==0) path.push("M");
+			else {
+				path.push("C");
+				var cx=x-(tension-1)*(dx/tension);
+				path.push(cx+","+dy);
+				cx=x-(dx/tension);
+				path.push(cx+","+y);
+			}
+			path.push(x+","+y);
+		}
+		line.setAttribute("d", path.join(" "));
+	};
+	plotters[types.Scatter]=function(series, chart){
+		var r=7;
+		for (var i=0; i<series.values.length; i++){
+			var x=_this.getX(series.values[i].x, chart);
+			var y=_this.getY(series.values[i].value, chart);
+			var point = document.createElementNS(dojo.svg.xmlns.svg, "path");
+			point.setAttribute("fill", series.color);
+			point.setAttribute("stroke-width", "0");
+			point.setAttribute("title", series.label + ": " + series.values[i].value);
+			point.setAttribute("d",
+				"M " + x + "," + (y-r) + " " +
+				"Q " + x + "," + y + " " + (x+r) + "," + y + " " +
+				"Q " + x + "," + y + " " + x + "," + (y+r) + " " +
+				"Q " + x + "," + y + " " + (x-r) + "," + y + " " +
+				"Q " + x + "," + y + " " + x + "," + (y-r) + " " +
+				"Z"
+			);
+			chart.dataGroup.appendChild(point);
+		}
+	};
+	plotters[types.Bubble]=function(series, chart){
+		//	added param for series[n].value: size
+		var minR=1;
+		
+		//	do this off the x axis?
+		var min=chart.properties.axes.x.range.min;
+		var max=chart.properties.axes.x.range.max;
+		var ofst=0-min;
+		min+=ofst; max+=ofst;
+
+		var xmin=chart.properties.padding.left;
+		var xmax=chart.properties.width-chart.properties.padding.right;
+		var factor=(max-min)/(xmax-xmin)*25;
+		
+		for (var i=0; i<series.values.length; i++){
+			var size = series.values[i].size;
+			if (isNaN(parseFloat(size))) size=minR;
+			var point=document.createElementNS(dojo.svg.xmlns.svg, "circle");
+			point.setAttribute("stroke-width", 0);
+			point.setAttribute("fill", series.color);
+			point.setAttribute("fill-opacity", "0.8");
+			point.setAttribute("r", (parseFloat(size)*factor)/2);
+			point.setAttribute("cx", _this.getX(series.values[i].x, chart));
+			point.setAttribute("cy", _this.getY(series.values[i].value, chart));
+			point.setAttribute("title", series.label + ": " + series.values[i].value + " (" + size + ")");
+			chart.dataGroup.appendChild(point);
+		}
+	};
+}();


Property changes on: trunk/PhotoOp/root/static/js/src/widget/svg/Chart.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/svg/HslColorPicker.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/svg/HslColorPicker.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/svg/HslColorPicker.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,139 @@
+/*
+	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.svg.HslColorPicker");
+
+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.svg.HslColorPicker=function(){
+	dojo.widget.HslColorPicker.call(this);
+	dojo.widget.HtmlWidget.call(this);
+	dojo.debug("warning: the HslColorPicker is not a finished widget, and is not yet ready for general use");
+	this.hue = "0";
+	this.saturation = "0";
+	this.light = "0";
+	this.storedColor = "#0054aa";
+	var _this = this;
+	this.filterObject = {
+	}
+};
+dojo.inherits(dojo.widget.svg.HslColorPicker, dojo.widget.HtmlWidget);
+dojo.lang.extend(dojo.widget.svg.HslColorPicker, {
+	//	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"));
+		saturation = parseInt(parseInt(xPosition)*(101/106));
+		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))
+			}
+			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;
+		}
+	}
+});

Added: trunk/PhotoOp/root/static/js/src/widget/templates/DemoEngine.html
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/DemoEngine.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/DemoEngine.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,24 @@
+<div dojoAttachPoint="domNode">
+	<div dojoAttachPoint="navigationNode">
+		<table border="0" cellspacing="0" cellpadding="0">
+			<tr>
+				<td width="1%" valign="top" class="navigationCell"><h1>Categories</h1><div dojoAttachPoint="menuNavigationNode"></div></td>
+				<td width="99%" valign="top">
+					<div dojoAttachPoint="demoNavigationNode">
+					</div>
+				</td>
+			</tr>
+		</table>
+	</div>
+
+	<div dojoAttachPoint="demoContainerNode">
+
+		<div dojoAttachPoint="demoPaneNode">
+		</div>
+
+		<div dojoAttachPoint="demoHeaderNode">
+			<span dojoAttachPoint="collapsedMenuNode" dojoAttachEvent="onclick: expandDemoNavigation"></span>
+			<div dojoAttachPoint="aboutNode">About this Demo</div>
+		</div>
+	</div>
+</div>


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/DemoEngine.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HslColorPicker.svg
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HslColorPicker.svg	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HslColorPicker.svg	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,30 @@
+<svg xmlns="http://www.w3.org/2000/svg"
+	xmlns:xlink="http://www.w3.org/1999/xlink"
+	version="1.1" baseProfile="full" width="170" height="131" xmlns:html="http://www.w3.org/1999/xhtml">
+	<defs>
+	<linearGradient id="colorGradient" dojoAttachPoint="colorGradientNode" x1="0" x2="131" y1="0" y2="0" gradientUnits="userSpaceOnUse">
+		<stop id="leftGradientColor" dojoAttachPoint="leftGradientColorNode" offset="0%" stop-color="#828282"/>
+		<stop id="rightGradientColor" dojoAttachPoint="rightGradientColorNode" offset="100%" stop-color="#053fff"/>
+	</linearGradient>
+	<linearGradient id="verticalGradient" x1="0" x2="0" y1="0" y2="131" gradientUnits="userSpaceOnUse">
+		<stop offset="0%" style="stop-color:#000000;"/>
+		<stop offset="50%" style="stop-color:#000000;stop-opacity:0;"/>
+		<stop offset="50%" style="stop-color:#ffffff;stop-opacity:0;"/>
+		<stop offset="100%" style="stop-color:#ffffff;"/>
+	</linearGradient>
+	<linearGradient id="sliderGradient">
+		<stop offset="0%" style="stop-color:#000000;"/>
+		<stop offset="15%" style="stop-color:#ffffff;"/>
+		<stop offset="30%" style="stop-color:#000000;"/>
+		<stop offset="45%" style="stop-color:#ffffff;"/>
+		<stop offset="60%" style="stop-color:#000000;"/>
+		<stop offset="75%" style="stop-color:#ffffff;"/>
+		<stop offset="90%" style="stop-color:#000000;"/>
+	</linearGradient>
+</defs>
+	<rect x="0" y="0" width="131px" height="131px" fill="url(#colorGradient)"/>
+	<rect x="0" y="0" width="131px" height="131px" style="fill:url(#verticalGradient);"/>
+	<rect id="saturationLightSlider" dojoAttachPoint="saturationLightSliderNode" x="100" y="100" width="5px" height="5px" style="stroke:url(#sliderGradient);stroke-width:1px;fill-opacity:0;"/>
+	<image xlink:href="images/hue.png" dojoAttachPoint="hueNode" x="140px" y="0px" width="21px" height="131px" dojoAttachEvent="onclick: onHueClick;"/>
+	<rect dojoAttachPoint="hueSliderNode" x="139px" y="40px" width="24px" height="4px" style="stroke-opacity:1;fill-opacity:0;stroke:black;"/>
+</svg>

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlButtonTemplate.css
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HtmlButtonTemplate.css	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HtmlButtonTemplate.css	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,59 @@
+/* ---- button --- */
+.dojoButton {
+	padding: 0 0 0 0;
+	font-size: 8pt;
+	white-space: nowrap;
+	cursor: pointer;
+	font-family: Myriad, Tahoma, Verdana, sans-serif;
+}
+
+.dojoButton .dojoButtonContents {
+	padding: 2px 2px 2px 2px;
+	text-align: center;		/* if icon and label are split across two lines, center icon */
+	color: white;
+}
+
+.dojoButtonLeftPart .dojoButtonContents {
+	padding-right: 8px;
+}
+
+.dojoButtonDisabled {
+	cursor: url("images/no.gif"), default;
+}
+
+
+.dojoButtonContents img {
+	vertical-align: middle;	/* if icon and label are on same line, center them */
+}
+
+/* -------- colors ------------ */
+
+.dojoButtonHover .dojoButtonContents {
+}
+
+.dojoButtonDepressed .dojoButtonContents {
+	color: #293a4b;
+}
+
+.dojoButtonDisabled .dojoButtonContents {
+	color: #eeeeee;
+}
+
+
+/* ---------- drop down button specific ---------- */
+
+/* border between label and arrow (for drop down buttons */
+.dojoButton .border {
+	width: 1px;
+	background: gray;
+}
+
+/* button arrow */
+.dojoButton .downArrow {
+	padding-left: 10px;
+	text-align: center;
+}
+
+.dojoButton.disabled .downArrow {
+	cursor : default;
+}
\ No newline at end of file


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlButtonTemplate.css
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlButtonTemplate.html
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HtmlButtonTemplate.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HtmlButtonTemplate.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,6 @@
+<div class="dojoButton" style="position:relative;" dojoAttachEvent="onMouseOver; onMouseOut; onMouseDown; onMouseUp; onClick:buttonClick;">
+  <div class="dojoButtonContents" align=center dojoAttachPoint="containerNode" style="position:absolute;z-index:2;"></div>
+  <img dojoAttachPoint="leftImage" style="position:absolute;left:0px;">
+  <img dojoAttachPoint="centerImage" style="position:absolute;z-index:1;">
+  <img dojoAttachPoint="rightImage" style="position:absolute;top:0px;right:0px;">
+</div>


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlButtonTemplate.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlCheckBox.html
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HtmlCheckBox.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HtmlCheckBox.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,6 @@
+<span>
+	<input type="checkbox" name="${this.name}" checked="${this.checked}" tabIndex="${this.tabIndex}" style="display: none"
+		dojoAttachPoint="inputNode">
+	<img src='${this.imgSrc}' class='dojoHtmlCheckbox'
+		dojoAttachPoint="imgNode" dojoAttachEvent="onMouseUp;onMouseOver;onMouseOut">
+</span>
\ No newline at end of file


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlCheckBox.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlCiviCrmDatePicker.html
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HtmlCiviCrmDatePicker.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HtmlCiviCrmDatePicker.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,12 @@
+<table cellpadding="0" cellspacing="0" border="0" width="400">
+	<tr>
+		<td id="dateHolderTd" width="200">
+		</td>
+		<td id="timeHolderTd" width="200">
+		</td>
+	</tr>
+	<tr style="display: none;" id="formItemsTr">
+		<td id="formItemsTd">&nbsp;</td>
+		<td>&nbsp;</td>
+	</tr>
+</table>


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlCiviCrmDatePicker.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlComboBox.css
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HtmlComboBox.css	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HtmlComboBox.css	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,40 @@
+input.dojoComboBoxInput {
+	/* font-size: 0.8em; */
+	border: 0px;
+	
+}
+
+.dojoComboBoxOptions {
+	font-family: Verdana, Helvetica, Garamond, sans-serif;
+	/* font-size: 0.7em; */
+	background-color: white;
+	border: 1px solid #afafaf;
+	position: absolute;
+	z-index: 1000; 
+	overflow: auto;
+	-moz-opacity: 0;
+	cursor: default;
+}
+
+table.dojoComboBox {
+	border: 1px solid #afafaf;
+}
+
+.dojoComboBoxItem {
+	padding-left: 2px;
+	padding-top: 2px;
+	margin: 0px;
+}
+
+.dojoComboBoxItemEven {
+	background-color: #f4f4f4;
+}
+
+.dojoComboBoxItemOdd {
+	background-color: white;
+}
+
+.dojoComboBoxItemHighlight {
+	background-color: #63709A;
+	color: white;
+}


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlComboBox.css
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlComboBox.html
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HtmlComboBox.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HtmlComboBox.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,34 @@
+<div style="position: relative; z-index: 100;">
+	<input type="hidden" name="" value="" 
+		dojoAttachPoint="comboBoxValue">
+	<input type="hidden" name="" value="" 
+		dojoAttachPoint="comboBoxSelectionValue">
+	<table class="dojoComboBox"
+		cellpadding="0"
+		cellmargin="0"
+		border="0"
+		dojoAttachPoint="cbTableNode">
+		<tr valign="top">
+			<td width=100%>
+				<input type="text" autocomplete="off" class="dojoComboBoxInput"
+					dojoAttachEvent="keyDown: onKeyDown; keyUp: onKeyUp; keyPress: onKeyPress;"
+					dojoAttachPoint="textInputNode"
+					style="width: 100%;">
+			</td>
+			<td>
+				<img border="0" 
+					hspace="0"
+					vspace="0"
+					dojoAttachPoint="downArrowNode"
+					dojoAttachEvent="onMouseUp: handleArrowClick;"
+					src="${dojoRoot}src/widget/templates/images/combo_box_arrow.png">
+			</td>
+		</tr>
+	</table>
+	<div dojoAttachPoint="optionsListWrapper" style="position:relative;" tabindex="-1">
+		<div class="dojoComboBoxOptions" dojoAttachPoint="optionsListNode"
+			dojoAttachEvent="onClick: selectOption;  onMouseOver: _onMouseOver; onMouseOut: _onMouseOut;"
+			style="display:none;" tabindex="-1">
+		</div>
+	</div>
+</div>


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlComboBox.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlComboButtonTemplate.html
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HtmlComboButtonTemplate.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HtmlComboButtonTemplate.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,18 @@
+<div class="dojoButton" style="position:relative;top:0px;left:0px; text-align:none;">
+
+	<div dojoAttachPoint="leftPart" class="dojoButtonLeftPart" style="position:absolute;left:0px;top:0px;"
+		dojoAttachEvent="onMouseOver:leftOver; onMouseOut:leftOut; onMouseUp:leftUp; onClick:leftClick;">
+		<div class="dojoButtonContents" dojoAttachPoint="containerNode" style="position:absolute;top:0px;right:0px;z-index:2;"></div>
+		<img dojoAttachPoint="leftImage" style="position:absolute;left:0px;top:0px;">
+		<img dojoAttachPoint="centerImage" style="position:absolute;right:0px;top:0px;z-index:1;">
+	</div>
+
+	<div dojoAttachPoint="rightPart" class="dojoButtonRightPart" style="position:absolute;top:0px;right:0px;"
+		dojoAttachEvent="onMouseOver:rightOver; onMouseOut:rightOut; onMouseUp:rightUp; onClick:rightClick;">
+		<img dojoAttachPoint="arrowBackgroundImage" style="position:absolute;top:0px;left:0px;z-index:1;">
+		<img src="${dojoRoot}src/widget/templates/images/whiteDownArrow.gif"
+		  		style="z-index:2;position:absolute;left:3px;top:50%;">
+		<img dojoAttachPoint="rightImage" style="position:absolute;top:0px;right:0px;">
+	</div>
+
+</div>


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlComboButtonTemplate.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlContextMenuTemplate.html
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HtmlContextMenuTemplate.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HtmlContextMenuTemplate.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,3 @@
+<ul class="dojoContextMenu" 
+	dojoAttachPoint="containerNode">
+</ul>


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlContextMenuTemplate.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlDatePicker.css
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HtmlDatePicker.css	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HtmlDatePicker.css	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,143 @@
+.calendarContainer {
+/*	border:1px solid #566f8f;*/
+}
+
+.calendarBodyContainer {
+	width:160px;
+	background: #7591bc url("images/dpBg.gif") top left repeat-x;
+}
+
+.calendarBodyContainer thead tr td {
+	color:#293a4b;
+	font:bold 0.75em Helvetica, Arial, Verdana, sans-serif;
+	text-align:center;
+	padding:0.25em;
+	background: url("images/dpHorizLine.gif") bottom left repeat-x;
+}
+
+.calendarBodyContainer tbody tr td {
+	color:#fff;
+	font:bold 0.7em Helvetica, Arial, Verdana, sans-serif;
+	text-align:center;
+	padding:0.4em;
+	background: url("images/dpVertLine.gif") top right repeat-y;
+	cursor:pointer;
+	cursor:hand;
+}
+
+
+.monthWrapper {
+	padding-bottom:2px;
+	background: url("images/dpHorizLine.gif") bottom left repeat-x;
+}
+
+.monthContainer {
+	width:100%;
+}
+
+.monthLabelContainer {
+	text-align:center;
+	font:bold 0.75em Helvetica, Arial, Verdana, sans-serif;
+	background: url("images/dpMonthBg.png") repeat-x top left !important;
+	color:#293a4b;
+	padding:0.25em;
+}
+
+.monthCurve {
+	width:12px;
+}
+
+.monthCurveTL {
+	background: url("images/dpCurveTL.png") no-repeat top left !important;
+}
+
+.monthCurveTR {
+		background: url("images/dpCurveTR.png") no-repeat top left !important;
+}
+
+
+.yearWrapper {
+	background: url("images/dpHorizLineFoot.gif") top left repeat-x;
+	padding-top:2px;
+}
+
+.yearContainer {
+	width:100%;
+}
+
+.yearContainer td {
+	background:url("images/dpYearBg.png") top left repeat-x;
+}
+
+.yearContainer .yearLabel {
+	margin:0;
+	padding:0.45em 0 0.45em 0;
+	color:#fff;
+	font:bold 0.75em Helvetica, Arial, Verdana, sans-serif;
+	text-align:center;
+}
+
+.curveBL {
+	background: url("images/dpCurveBL.png") bottom left no-repeat !important;
+	width:9px !important;
+	padding:0;
+	margin:0;
+}
+
+.curveBR {
+	background: url("images/dpCurveBR.png") bottom left no-repeat !important;
+	width:9px !important;
+	padding:0;
+	margin:0;
+}
+
+
+.previousMonth {
+	background-color:#6782a8 !important;
+}
+
+.currentMonth {
+}
+
+.nextMonth {
+	background-color:#6782a8 !important;
+}
+
+.currentDate {
+	text-decoration:underline;
+	font-style:italic;
+}
+
+.selectedItem {
+	background-color:#fff !important;
+	color:#6782a8 !important;
+}
+
+.yearLabel .selectedYear {
+	padding:0.2em;
+	background-color:#9ec3fb !important;
+}
+
+.nextYear, .previousYear {
+	cursor:pointer;cursor:hand;
+	margin:0 0.55em;
+}
+
+.incrementControl {
+	cursor:pointer;cursor:hand;
+	width:1em;
+}
+
+.increase {
+	float:right;
+}
+
+.decrease {
+	float:left;
+}
+
+.lastColumn {
+	background-image:none !important;
+}
+
+


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlDatePicker.css
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlDatePicker.html
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HtmlDatePicker.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HtmlDatePicker.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,141 @@
+<div class="datePickerContainer" dojoAttachPoint="datePickerContainerNode">
+	<table cellspacing="0" cellpadding="0" class="calendarContainer">
+		<thead>
+			<tr>
+				<td class="monthWrapper" valign="top">
+					<table class="monthContainer" cellspacing="0" cellpadding="0" border="0">
+						<tr>
+							<td class="monthCurve monthCurveTL" valign="top"></td>
+							<td class="monthLabelContainer" valign="top">
+								<span dojoAttachPoint="increaseWeekNode" 
+									dojoAttachEvent="onClick: onIncrementWeek;" 
+									class="incrementControl increase">
+									<img src="${dojoRoot}src/widget/templates/images/incrementMonth.png" 
+									alt="&darr;" style="width:7px;height:5px;" />
+								</span>
+								<span 
+									dojoAttachPoint="increaseMonthNode" 
+									dojoAttachEvent="onClick: onIncrementMonth;" class="incrementControl increase">
+									<img src="${dojoRoot}src/widget/templates/images/incrementMonth.png" 
+										alt="&darr;"  dojoAttachPoint="incrementMonthImageNode">
+								</span>
+								<span 
+									dojoAttachPoint="decreaseWeekNode" 
+									dojoAttachEvent="onClick: onIncrementWeek;" 
+									class="incrementControl decrease">
+									<img src="${dojoRoot}src/widget/templates/images/decrementMonth.png" alt="&uarr;" style="width:7px;height:5px;" />
+								</span>
+								<span 
+									dojoAttachPoint="decreaseMonthNode" 
+									dojoAttachEvent="onClick: onIncrementMonth;" class="incrementControl decrease">
+									<img src="${dojoRoot}src/widget/templates/images/decrementMonth.png" 
+										alt="&uarr;" dojoAttachPoint="decrementMonthImageNode">
+								</span>
+								<span dojoAttachPoint="monthLabelNode" class="month">July</span>
+							</td>
+							<td class="monthCurve monthCurveTR" valign="top"></td>
+						</tr>
+					</table>
+				</td>
+			</tr>
+		</thead>
+		<tbody>
+			<tr>
+				<td colspan="3">
+					<table class="calendarBodyContainer" cellspacing="0" cellpadding="0" border="0">
+						<thead>
+							<tr>
+								<td>S</td>
+								<td>M</td>
+								<td>T</td>
+								<td>W</td>
+								<td>T</td>
+								<td>F</td>
+								<td>S</td>
+							</tr>
+						</thead>
+						<tbody dojoAttachPoint="calendarDatesContainerNode" 
+							dojoAttachEvent="onClick: onSetDate;">
+							<tr dojoAttachPoint="calendarRow0">
+								<td></td>
+								<td></td>
+								<td></td>
+								<td></td>
+								<td></td>
+								<td></td>
+								<td></td>
+							</tr>
+							<tr dojoAttachPoint="calendarRow1">
+								<td></td>
+								<td></td>
+								<td></td>
+								<td></td>
+								<td></td>
+								<td></td>
+								<td></td>
+							</tr>
+							<tr dojoAttachPoint="calendarRow2">
+								<td></td>
+								<td></td>
+								<td></td>
+								<td></td>
+								<td></td>
+								<td></td>
+								<td></td>
+							</tr>
+							<tr dojoAttachPoint="calendarRow3">
+								<td></td>
+								<td></td>
+								<td></td>
+								<td></td>
+								<td></td>
+								<td></td>
+								<td></td>
+							</tr>
+							<tr dojoAttachPoint="calendarRow4">
+								<td></td>
+								<td></td>
+								<td></td>
+								<td></td>
+								<td></td>
+								<td></td>
+								<td></td>
+							</tr>
+							<tr dojoAttachPoint="calendarRow5">
+								<td></td>
+								<td></td>
+								<td></td>
+								<td></td>
+								<td></td>
+								<td></td>
+								<td></td>
+							</tr>
+						</tbody>
+					</table>
+				</td>
+			</tr>
+		</tbody>
+		<tfoot>
+			<tr>
+				<td colspan="3" class="yearWrapper">
+					<table cellspacing="0" cellpadding="0" border="0" class="yearContainer">
+						<tr>
+							<td class="curveBL" valign="top"></td>
+							<td valign="top">
+								<h3 class="yearLabel">
+									<span dojoAttachPoint="previousYearLabelNode"
+										dojoAttachEvent="onClick: onIncrementYear;" class="previousYear"></span>
+									<span class="selectedYear" dojoAttachPoint="currentYearLabelNode"></span>
+									<span dojoAttachPoint="nextYearLabelNode" 
+										dojoAttachEvent="onClick: onIncrementYear;" class="nextYear"></span>
+								</h3>
+							</td>
+							<td class="curveBR" valign="top"></td>
+						</tr>
+					</table>
+				</td>
+			</tr>
+		</tfoot>
+	</table>
+	
+</div>


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlDatePicker.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlDialog.html
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HtmlDialog.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HtmlDialog.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,13 @@
+<div id="${this.widgetId}" class="dojoDialog" dojoAttachPoint="wrapper">
+
+	<span dojoAttachPoint="tabStart" 
+		dojoOnFocus="trapTabs" 
+		dojoOnBlur="clearTrap" tabindex="0"></span>
+
+	<div dojoAttachPoint="containerNode" style=" position: relative; z-index: 2;"></div>
+
+	<span dojoAttachPoint="tabEnd" 
+		dojoOnFocus="trapTabs" 
+		dojoOnBlur="clearTrap" tabindex="0"></span>
+
+</div>


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlDialog.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlDocPane.css
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HtmlDocPane.css	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HtmlDocPane.css	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,19 @@
+.dojoDocSrc {
+	white-space: pre;
+	font-family: Courier, "Courier New", monospace;
+	font-size: 0.8em;
+}
+.dojoDocReturn {
+	color: #369;
+}
+.dojoDocFunction {
+	color: #98543F;
+	font-weight: bold;
+}
+.dojoDocParamType {
+	font-style: italic;
+	color: #D17575;
+}
+.dojoDocParamName {
+	color: #D14040;
+}
\ No newline at end of file


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlDocPane.css
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlDocPane.html
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HtmlDocPane.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HtmlDocPane.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,23 @@
+<div>
+	<div dojoAttachPoint="containerNode"></div>
+	<div dojoAttachPoint="select">
+		<h1>Detail: <span dojoAttachPoint="fn">dojo.select</span></h1>
+		<div dojoAttachPoint="description">Description</div>
+		<div class="dojoDocParams" dojoAttachPoint="parameters">
+			<h2>Parameters</h2>
+			<div dojoAttachPoint="pRow"><span dojoAttachPoint="pOpt"><em>optional</em> </span><span><span dojoAttachPoint="pType">type</span> </span><a href="#" dojoAttachPoint="pLink">variable</a><span> - <span dojoAttachPoint="pDesc"></span></span></div>
+		</div>
+		<div class="dojoDocVars" dojoAttachPoint="variables">
+			<h2>Variables</h2>
+			<div dojoAttachPoint="vRow"><a href="#" dojoAttachPoint="vLink">variable</a><span> - <span dojoAttachPoint="vDesc"></span></span></div>
+		</div>
+		<div>
+			<h2>Source Code</h2>
+			<div class="dojoDocSrc"><span class="dojoDocReturn" dojoAttachPoint="sType">returnType</span> <span class="dojoDocFunction" dojoAttachPoint="sName">foo</span>(<span dojoAttachPoint="sParams"><span class="dojoDocParamType" dojoAttachPoint="sPType">type </span><span class="dojoDocParamName" dojoAttachPoint="sPName">paramName</span></span>){<span dojoAttachPoint="source">...</span>}</div>
+		</div>
+	</div>
+	<div dojoAttachPoint="result">
+		<h1>Search Results: <span dojoAttachPoint="count">0</span> matches</h1>
+		<div dojoAttachPoint="row"><a href="#" dojoAttachPoint="fnLink">dojo.fnLink</a><span> - <span dojoAttachPoint="summary">summary</span></span></div>
+	</div>
+</div>


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlDocPane.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlDropDownButtonTemplate.html
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HtmlDropDownButtonTemplate.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HtmlDropDownButtonTemplate.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,9 @@
+<button dojoAttachPoint="button" class="dojoButton dojoButtonNoHover" dojoAttachEvent="onMouseOver: ; onMouseOut: ; onClick: ;">
+  <table dojoAttachPoint="table" style="margin:0 0 0 0;"><tr>
+    <td class="label" dojoAttachPoint="labelCell"></td>
+    <td class="border" dojoAttachPoint="borderCell"></td>
+    <td class="downArrow" dojoAttachPoint="arrowCell">
+      <img dojoAttachPoint="arrow">
+    </td>
+  </tr></table>
+</button>
\ No newline at end of file


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlDropDownButtonTemplate.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlEditorToolbar.css
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HtmlEditorToolbar.css	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HtmlEditorToolbar.css	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,138 @@
+.StyleDropdownContainer {
+	position: absolute;
+	z-index: 1000;
+	overflow: auto;
+	cursor: default;
+	width: 250px;
+	height: 250px;
+	background-color: white;
+	border: 1px solid black;
+}
+
+.ColorDropdownContainer {
+	position: absolute;
+	z-index: 1000;
+	overflow: auto;
+	cursor: default;
+	width: 250px;
+	height: 150px;
+	background-color: white;
+	border: 1px solid black;
+}
+
+.EditorToolbarDomNode {
+	background-image: url(buttons/bg-fade.png);
+	background-repeat: repeat-x;
+	background-position: 0px -50px;
+}
+
+.EditorToolbarSmallBg {
+	background-image: url(images/toolbar-bg.gif);
+	background-repeat: repeat-x;
+	background-position: 0px 0px;
+}
+
+/*
+body {
+	background:url(images/blank.gif) fixed;
+}*/
+
+.IEFixedToolbar {
+	position:absolute;
+	/* top:0; */
+	top: expression(eval((document.documentElement||document.body).scrollTop));
+}
+
+div.bigIcon {
+	width: 40px;
+	height: 40px; 
+	/* background-color: white; */
+	/* border: 1px solid #a6a7a3; */
+	font-family: Verdana, Trebuchet, Tahoma, Arial;
+}
+
+.iconContainer {
+	font-family: Verdana, Trebuchet, Tahoma, Arial;
+	font-size: 13px;
+	float: left;
+	height: 18px;
+	display: block;
+	/* background-color: white; */
+	/* border: 1px solid white; */
+	/* border: 1px solid #a6a7a3; */
+	padding-right: 3px;
+	cursor: pointer;
+	border: 1px solid transparent;
+	_border: none;
+}
+
+span.icon {
+	display: block;
+	text-align: center;
+	min-width: 18px;
+	width: 18px;
+	height: 18px;
+	/* background-color: #a6a7a3; */
+	background-repeat: no-repeat;
+	background-image: url(buttons/aggregate.gif);
+}
+
+
+span.icon[class~=icon] {
+}
+
+.headingContainer {
+	width: 150px;
+	height: 30px;
+	margin: 0px;
+	/* padding-left: 5px; */
+	overflow: hidden;
+	line-height: 25px;
+	border-bottom: 1px solid black;
+	border-top: 1px solid white;
+}
+
+.EditorToolbarDomNode select {
+	font-size: 14px;
+}
+
+.sep { width: 5px; min-width: 5px; max-width: 5px; background-position: 0px 0px}
+.backcolor { background-position: -18px 0px}
+.bold { background-position: -36px 0px}
+.cancel { background-position: -54px 0px}
+.copy { background-position: -72px 0px}
+.createlink { background-position: -90px 0px}
+.cut { background-position: -108px 0px}
+.delete { background-position: -126px 0px}
+.forecolor { background-position: -144px 0px}
+.hilitecolor { background-position: -162px 0px}
+.indent { background-position: -180px 0px}
+.inserthorizontalrule { background-position: -198px 0px}
+.insertimage { background-position: -216px 0px}
+.insertorderedlist { background-position: -234px 0px}
+.inserttable { background-position: -252px 0px}
+.insertunorderedlist { background-position: -270px 0px}
+.italic { background-position: -288px 0px}
+.justifycenter { background-position: -306px 0px}
+.justifyfull { background-position: -324px 0px}
+.justifyleft { background-position: -342px 0px}
+.justifyright { background-position: -360px 0px}
+.left_to_right { background-position: -378px 0px}
+.list_bullet_indent { background-position: -396px 0px}
+.list_bullet_outdent { background-position: -414px 0px}
+.list_num_indent { background-position: -432px 0px}
+.list_num_outdent { background-position: -450px 0px}
+.outdent { background-position: -468px 0px}
+.paste { background-position: -486px 0px}
+.redo { background-position: -504px 0px}
+.removeformat { background-position: -522px 0px}
+.right_to_left { background-position: -540px 0px}
+.save { background-position: -558px 0px}
+.space { background-position: -576px 0px}
+.strikethrough { background-position: -594px 0px}
+.subscript { background-position: -612px 0px}
+.superscript { background-position: -630px 0px}
+.underline { background-position: -648px 0px}
+.undo { background-position: -666px 0px}
+.wikiword { background-position: -684px 0px}
+


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlEditorToolbar.css
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlEditorToolbar.html
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HtmlEditorToolbar.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HtmlEditorToolbar.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,152 @@
+<div dojoAttachPoint="domNode" class="EditorToolbarDomNode" unselectable="on">
+	<table cellpadding="3" cellspacing="0" border="0">
+		<!--
+			our toolbar should look something like:
+
+			+=======+=======+=======+=============================================+
+			| w   w | style | copy  | bo | it | un | le | ce | ri |
+			| w w w | style |=======|==============|==============|
+			|  w w  | style | paste |  undo | redo | change style |
+			+=======+=======+=======+=============================================+
+		-->
+		<tbody>
+			<tr valign="top">
+				<td rowspan="2">
+					<div class="bigIcon" dojoAttachPoint="wikiWordButton"
+						dojoOnClick="wikiWordClick; buttonClick;">
+						<span style="font-size: 30px; margin-left: 5px;">
+							W
+						</span>
+					</div>
+				</td>
+				<td rowspan="2">
+					<div class="bigIcon" dojoAttachPoint="styleDropdownButton"
+						dojoOnClick="styleDropdownClick; buttonClick;">
+						<span unselectable="on"
+							style="font-size: 30px; margin-left: 5px;">
+							S
+						</span>
+					</div>
+					<div class="StyleDropdownContainer" style="display: none;"
+						dojoAttachPoint="styleDropdownContainer">
+						<table cellpadding="0" cellspacing="0" border="0"
+							height="100%" width="100%">
+							<tr valign="top">
+								<td rowspan="2">
+									<div style="height: 245px; overflow: auto;">
+										<div class="headingContainer"
+											unselectable="on"
+											dojoOnClick="normalTextClick">normal</div>
+										<h1 class="headingContainer"
+											unselectable="on"
+											dojoOnClick="h1TextClick">Heading 1</h1>
+										<h2 class="headingContainer"
+											unselectable="on"
+											dojoOnClick="h2TextClick">Heading 2</h2>
+										<h3 class="headingContainer"
+											unselectable="on"
+											dojoOnClick="h3TextClick">Heading 3</h3>
+										<h4 class="headingContainer"
+											unselectable="on"
+											dojoOnClick="h4TextClick">Heading 4</h4>
+										<div class="headingContainer"
+											unselectable="on"
+											dojoOnClick="blahTextClick">blah</div>
+										<div class="headingContainer"
+											unselectable="on"
+											dojoOnClick="blahTextClick">blah</div>
+										<div class="headingContainer"
+											unselectable="on"
+											dojoOnClick="blahTextClick">blah</div>
+										<div class="headingContainer">blah</div>
+										<div class="headingContainer">blah</div>
+										<div class="headingContainer">blah</div>
+										<div class="headingContainer">blah</div>
+									</div>
+								</td>
+								<!--
+								<td>
+									<span class="iconContainer" dojoOnClick="buttonClick;">
+										<span class="icon justifyleft" 
+											style="float: left;">&nbsp;</span>
+									</span>
+									<span class="iconContainer" dojoOnClick="buttonClick;">
+										<span class="icon justifycenter" 
+											style="float: left;">&nbsp;</span>
+									</span>
+									<span class="iconContainer" dojoOnClick="buttonClick;">
+										<span class="icon justifyright" 
+											style="float: left;">&nbsp;</span>
+									</span>
+									<span class="iconContainer" dojoOnClick="buttonClick;">
+										<span class="icon justifyfull" 
+											style="float: left;">&nbsp;</span>
+									</span>
+								</td>
+								-->
+							</tr>
+							<tr valign="top">
+								<td>
+									thud
+								</td>
+							</tr>
+						</table>
+					</div>
+				</td>
+				<td>
+					<!-- copy -->
+					<span class="iconContainer" dojoAttachPoint="copyButton"
+						unselectable="on"
+						dojoOnClick="copyClick; buttonClick;">
+						<span class="icon copy" 
+							unselectable="on"
+							style="float: left;">&nbsp;</span> copy
+					</span>
+					<!-- "droppable" options -->
+					<span class="iconContainer" dojoAttachPoint="boldButton"
+						unselectable="on"
+						dojoOnClick="boldClick; buttonClick;">
+						<span class="icon bold" unselectable="on">&nbsp;</span>
+					</span>
+					<span class="iconContainer" dojoAttachPoint="italicButton"
+						dojoOnClick="italicClick; buttonClick;">
+						<span class="icon italic" unselectable="on">&nbsp;</span>
+					</span>
+					<span class="iconContainer" dojoAttachPoint="underlineButton"
+						dojoOnClick="underlineClick; buttonClick;">
+						<span class="icon underline" unselectable="on">&nbsp;</span>
+					</span>
+					<span class="iconContainer" dojoAttachPoint="leftButton"
+						dojoOnClick="leftClick; buttonClick;">
+						<span class="icon justifyleft" unselectable="on">&nbsp;</span>
+					</span>
+					<span class="iconContainer" dojoAttachPoint="fullButton"
+						dojoOnClick="fullClick; buttonClick;">
+						<span class="icon justifyfull" unselectable="on">&nbsp;</span>
+					</span>
+					<span class="iconContainer" dojoAttachPoint="rightButton"
+						dojoOnClick="rightClick; buttonClick;">
+						<span class="icon justifyright" unselectable="on">&nbsp;</span>
+					</span>
+				</td>
+			</tr>
+			<tr>
+				<td>
+					<!-- paste -->
+					<span class="iconContainer" dojoAttachPoint="pasteButton"
+						dojoOnClick="pasteClick; buttonClick;" unselectable="on">
+						<span class="icon paste" style="float: left;" unselectable="on">&nbsp;</span> paste
+					</span>
+					<!-- "droppable" options -->
+					<span class="iconContainer" dojoAttachPoint="undoButton"
+						dojoOnClick="undoClick; buttonClick;" unselectable="on">
+						<span class="icon undo" style="float: left;" unselectable="on">&nbsp;</span> undo
+					</span>
+					<span class="iconContainer" dojoAttachPoint="redoButton"
+						dojoOnClick="redoClick; buttonClick;" unselectable="on">
+						<span class="icon redo" style="float: left;" unselectable="on">&nbsp;</span> redo
+					</span>
+				</td>	
+			</tr>
+		</tbody>
+	</table>


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlEditorToolbar.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlEditorToolbarOneline.html
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HtmlEditorToolbarOneline.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HtmlEditorToolbarOneline.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,266 @@
+<div class="EditorToolbarDomNode EditorToolbarSmallBg" 
+	unselectable="on" dojoOnMouseDown="preventSelect">
+	<table cellpadding="1" cellspacing="0" border="0">
+		<!--
+			toobar options, in order:
+
+				* wikiword
+				* paste
+				* copy
+				* undo
+				* redo
+				* link
+				* insert image
+				* insert <hr>
+				* font-select
+				* bold
+				* italic
+				* underline
+				* strikethrough
+				* numlist
+				* dotlist
+				* indent
+				* outdent
+				* text color
+				* bg color
+
+		-->
+		<tbody>
+			<tr valign="top" align="left" dojoAttachPoint="oneLineTr">
+				<td>
+					<!-- htmltoggle -->
+					<span class="iconContainer" dojoAttachPoint="htmltoggleButton"
+						dojoOnClick="htmltoggleClick; buttonClick;" style="display: none;">
+						<span title="Toggle Rich Text and HTML Markup Editing Modes" class="icon" 
+						style="background-image: none; width: 30px;" 
+						unselectable="on">&lt;h&gt;</span>
+					</span>
+				</td>
+				<td>
+					<!-- wikiword -->
+					<span class="iconContainer" dojoAttachPoint="wikiwordButton"
+						dojoOnClick="wikiwordClick; buttonClick;" style="display: none;">
+						<span title="Toggle Wiki Word" class="icon wikiword" 
+						unselectable="on">&nbsp;</span>
+					</span>
+				</td>
+				<td>
+					<!-- paste -->
+					<span class="iconContainer" dojoAttachPoint="copyButton"
+						unselectable="on" dojoOnClick="copyClick; buttonClick;">
+						<span title="Copy (Ctrl-C)" class="icon copy" unselectable="on">&nbsp;</span>
+					</span>
+				</td>
+				<td>
+					<!-- copy -->
+					<span class="iconContainer" dojoAttachPoint="pasteButton"
+						dojoOnClick="pasteClick; buttonClick;" unselectable="on">
+						<span title="Paste (Ctrl-V)" class="icon paste" unselectable="on">&nbsp;</span>
+					</span>
+				</td>
+				<td>
+					<!-- undo -->
+					<span class="iconContainer" dojoAttachPoint="undoButton"
+						dojoOnClick="undoClick; buttonClick;" unselectable="on">
+						<!-- FIXME: should we have the text "undo" here? -->
+						<span title="Undo (Ctrl-Z)" class="icon undo" unselectable="on">&nbsp;</span>
+					</span>
+				</td>
+				<td>
+					<!-- redo -->
+					<span class="iconContainer" dojoAttachPoint="redoButton"
+						dojoOnClick="redoClick; buttonClick;" unselectable="on">
+						<span title="Redo (Ctrl-R)" class="icon redo" unselectable="on">&nbsp;</span>
+					</span>
+				</td>
+				<td isSpacer="true">
+					<!-- spacer -->
+					<span class="iconContainer">
+						<span class="icon sep" unselectable="on" 
+							style="width: 5px; min-width: 5px;"></span>
+					</span>
+				</td>
+				<td>
+					<!-- link -->
+					<span class="iconContainer" dojoAttachPoint="linkButton"
+						unselectable="on" dojoOnClick="linkClick; buttonClick;">
+						<span title="Create Link" class="icon createlink" unselectable="on">&nbsp;</span>
+					</span>
+				</td>
+				<td>
+					<!-- insertimage -->
+					<span class="iconContainer" 
+						style="display: none;"
+						dojoAttachPoint="insertimageButton"
+						unselectable="on" dojoOnClick="insertimageClick; buttonClick;">
+						<span title="Insert Image" class="icon insertimage" unselectable="on">&nbsp;</span>
+					</span>
+				</td>
+				<td>
+					<!-- inserthorizontalrule -->
+					<span class="iconContainer" dojoAttachPoint="inserthorizontalruleButton"
+						unselectable="on" dojoOnClick="inserthorizontalruleClick; buttonClick;">
+						<span title="Insert Horizontal Rule" class="icon inserthorizontalrule " unselectable="on">&nbsp;</span>
+					</span>
+				</td>
+				<td>
+					<!-- bold -->
+					<span class="iconContainer" dojoAttachPoint="boldButton"
+						unselectable="on" dojoOnClick="boldClick; buttonClick;">
+						<span title="Toggle Bold (Ctrl-B)" class="icon bold" unselectable="on">&nbsp;</span>
+					</span>
+				</td>
+				<td>
+					<!-- italic -->
+					<span class="iconContainer" dojoAttachPoint="italicButton"
+						dojoOnClick="italicClick; buttonClick;">
+						<span title="Toggle Italic (Ctrl-I)" class="icon italic" unselectable="on">&nbsp;</span>
+					</span>
+				</td>
+				<td>
+					<!-- underline -->
+					<span class="iconContainer" dojoAttachPoint="underlineButton"
+						dojoOnClick="underlineClick; buttonClick;">
+						<span title="Toggle Underline (Ctrl-U)" class="icon underline" unselectable="on">&nbsp;</span>
+					</span>
+				</td>
+				<td>
+					<!-- strikethrough -->
+					<span class="iconContainer" dojoAttachPoint="strikethroughButton"
+						dojoOnClick="strikethroughClick; buttonClick;">
+						<span title="Toggle Strike-Through" 
+							class="icon strikethrough" unselectable="on">&nbsp;</span>
+					</span>
+				</td>
+				<td isSpacer="true">
+					<!-- spacer -->
+					<span class="iconContainer">
+						<span class="icon sep" unselectable="on" 
+							style="width: 5px; min-width: 5px;"></span>
+					</span>
+				</td>
+				<td>
+					<!-- insertunorderedlist -->
+					<span class="iconContainer" dojoAttachPoint="insertunorderedlistButton"
+						unselectable="on" 
+						dojoOnClick="insertunorderedlistClick; buttonClick;">
+						<span title="Insert Bullet List" 
+							class="icon insertunorderedlist" unselectable="on">&nbsp;</span>
+					</span>
+				</td>
+				<td>
+					<!-- insertorderedlist -->
+					<span class="iconContainer" dojoAttachPoint="insertorderedlistButton"
+						unselectable="on" dojoOnClick="insertorderedlistClick; buttonClick;">
+						<span title="Insert Numbered List" 
+							class="icon insertorderedlist" unselectable="on">&nbsp;</span>
+					</span>
+				</td>
+				<td isSpacer="true">
+					<!-- spacer -->
+					<span class="iconContainer">
+						<span class="icon sep" unselectable="on" 
+							style="width: 5px; min-width: 5px;"></span>
+					</span>
+				</td>
+				<td>
+					<!-- indent -->
+					<span class="iconContainer" dojoAttachPoint="indentButton"
+						unselectable="on" dojoOnClick="indentClick; buttonClick;">
+						<span title="Indent Selection" class="icon indent" 
+							unselectable="on">&nbsp;</span>
+					</span>
+				</td>
+				<td>
+					<!-- outdent -->
+					<span class="iconContainer" dojoAttachPoint="outdentButton"
+						unselectable="on" dojoOnClick="outdentClick; buttonClick;">
+						<span title="Outdent Selection" class="icon outdent" 
+							unselectable="on">&nbsp;</span>
+					</span>
+				</td>
+				<td isSpacer="true">
+					<!-- spacer -->
+					<span class="iconContainer">
+						<span class="icon sep" unselectable="on" 
+							style="width: 5px; min-width: 5px;"></span>
+					</span>
+				</td>
+				<td>
+					<!-- forecolor -->
+					<span class="iconContainer" dojoAttachPoint="forecolorButton"
+						unselectable="on" dojoOnClick="forecolorClick; buttonClick;">
+						<span title="Change Text Color" class="icon forecolor" 
+							unselectable="on">&nbsp;</span>
+					</span>
+					<div class="ColorDropdownContainer" 
+						style="display: none; margin-top: 22px;"
+						dojoAttachPoint="forecolorDropDown">
+					</div>
+				</td>
+				<td>
+					<!-- hilitecolor -->
+					<span class="iconContainer" dojoAttachPoint="hilitecolorButton"
+						unselectable="on" dojoOnClick="hilitecolorClick; buttonClick;">
+						<span title="Change Background Color" class="icon hilitecolor" 
+							unselectable="on">&nbsp;</span>
+					</span>
+					<div class="ColorDropdownContainer" 
+						style="display: none; margin-top: 22px;"
+						dojoAttachPoint="hilitecolorDropDown">
+					</div>
+				</td>
+				<td isSpacer="true">
+					<!-- spacer -->
+					<span class="iconContainer">
+						<span class="icon sep" unselectable="on" 
+							style="width: 5px; min-width: 5px;"></span>
+					</span>
+				</td>
+				<td>
+					<!-- justify left -->
+					<span class="iconContainer" dojoAttachPoint="justifyleftButton"
+						dojoOnClick="justifyleftClick; buttonClick;">
+						<span title="Justify Left" class="icon justifyleft" unselectable="on">&nbsp;</span>
+					</span>
+				</td>
+				<td>
+					<!-- justify center -->
+					<span class="iconContainer" dojoAttachPoint="justifycenterButton"
+						dojoOnClick="justifycenterClick; buttonClick;">
+						<span title="Center" class="icon justifycenter" unselectable="on">&nbsp;</span>
+					</span>
+				</td>
+				<td>
+					<!-- justify right -->
+					<span class="iconContainer" dojoAttachPoint="justifyrightButton"
+						dojoOnClick="justifyrightClick; buttonClick;">
+						<span title="Justify Right" class="icon justifyright" unselectable="on">&nbsp;</span>
+					</span>
+				</td>
+				<td>
+					<!-- justify full -->
+					<span class="iconContainer" dojoAttachPoint="justifyfullButton"
+						dojoOnClick="justifyfullClick; buttonClick;">
+						<span title="Justify Both" class="icon justifyfull" unselectable="on">&nbsp;</span>
+					</span>
+				</td>	
+				<td>
+					<!-- font select -->
+					<select dojoOnChange="formatSelectClick; buttonClick;"
+						dojoAttachPoint="formatSelectBox" unselectable="on">
+						<!-- FIXME: using "p" here inserts a paragraph in most cases! -->
+						<option value="">-- format --</option>
+						<option value="p">Normal</option>
+						<option value="pre">Fixed Font</option>
+						<option value="h1">Main Heading</option>
+						<option value="h2">Section Heading</option>
+						<option value="h3">Sub-Heading</option>
+						<!-- <option value="blockquote">Block Quote</option> -->
+					</select>
+				</td>
+				<td width="*">&nbsp;</td>
+			</tr>
+		</tbody>
+	</table>
+</div>


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlEditorToolbarOneline.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlFisheyeList.css
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HtmlFisheyeList.css	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HtmlFisheyeList.css	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,27 @@
+.dojoHtmlFisheyeListItemLabel {
+	font-family: Arial, Helvetica, sans-serif;
+	background-color: #eee;
+	border: 2px solid #666;
+	padding: 2px;
+	text-align: center;
+	position: absolute;
+	display: none;
+}
+
+.dojoHtmlFisheyeListItemLabel.selected {
+	display: block;
+}
+
+.dojoHtmlFisheyeListItemImage {
+	border: 0px;
+	position: absolute;
+}
+
+.dojoHtmlFisheyeListItem {
+	position: absolute;
+	z-index: 2;
+}
+
+.dojoHtmlFisheyeListBar {
+	position: relative;
+}


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlFisheyeList.css
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlFloatingPane.css
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HtmlFloatingPane.css	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HtmlFloatingPane.css	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,118 @@
+
+/********** Outer Window ***************/
+
+.dojoFloatingPane {
+	/* essential css */
+	position: absolute;
+	overflow: visible;		/* so drop shadow is displayed */
+	z-index: 10;
+
+	/* styling css */
+	border: 1px solid;
+	border-color: ThreeDHighlight ThreeDShadow ThreeDShadow ThreeDHighlight;
+	background-color: ThreeDFace;
+}
+
+
+/********** Title Bar ****************/
+
+.dojoFloatingPaneTitleBar {
+	vertical-align: top;
+	margin: 2px 2px 2px 2px;
+	z-index: 10;
+	background-color: #7596c6;
+	cursor: default;
+	overflow: hidden;
+	border-color: ThreeDHighlight ThreeDShadow ThreeDShadow ThreeDHighlight;
+	vertical-align: middle;
+}
+
+.dojoFloatingPaneTitleText {
+	float: left;
+	padding: 2px 4px 2px 2px;
+	white-space: nowrap;
+	color: CaptionText;
+	font: small-caption;
+}
+
+.dojoTitleBarIcon {
+	float: left;
+	height: 22px;
+	width: 22px;
+	vertical-align: middle;
+	margin-right: 5px;
+	margin-left: 5px;
+}
+
+.dojoFloatingPaneActions{
+	float: right;
+	position: absolute;
+	right: 2px;
+	top: 2px;
+	vertical-align: middle;
+}
+
+
+.dojoFloatingPaneActionItem {
+	vertical-align: middle;
+	margin-right: 1px;
+	height: 22px;
+	width: 22px;
+}
+
+
+.dojoFloatingPaneTitleBarIcon {
+	/* essential css */
+	float: left;
+
+	/* styling css */
+	margin-left: 2px;
+	margin-right: 4px;
+	height: 22px;
+}
+
+/* minimize/maximize icons are specified by CSS only */
+.dojoFloatingPaneMinimizeIcon,
+.dojoFloatingPaneMaximizeIcon,
+.dojoFloatingPaneRestoreIcon,
+.dojoFloatingPaneCloseIcon {
+	vertical-align: middle;
+	height: 22px;
+	width: 22px;
+	float: right;
+}
+.dojoFloatingPaneMinimizeIcon {
+	background-image: url(images/floatingPaneMinimize.gif);
+}
+.dojoFloatingPaneMaximizeIcon {
+	background-image: url(images/floatingPaneMaximize.gif);
+}
+.dojoFloatingPaneRestoreIcon {
+	background-image: url(images/floatingPaneRestore.gif);
+}
+.dojoFloatingPaneCloseIcon {
+	background-image: url(images/floatingPaneClose.gif);
+}
+
+/* bar at bottom of window that holds resize handle */
+.dojoFloatingPaneResizebar {
+	z-index: 10;
+	height: 13px;
+	background-color: ThreeDFace;
+}
+
+/************* Client Area ***************/
+
+.dojoFloatingPaneClient {
+	position: relative;
+	z-index: 10;
+	border: 1px solid;
+	border-color: ThreeDShadow ThreeDHighlight ThreeDHighlight ThreeDShadow;
+	margin: 2px;
+	background-color: ThreeDFace;
+	padding: 8px;
+	font-family: Verdana, Helvetica, Garamond, sans-serif;
+	font-size: 12px;
+	overflow: auto;
+}
+


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlFloatingPane.css
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlFloatingPane.html
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HtmlFloatingPane.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HtmlFloatingPane.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,18 @@
+<div id="${this.widgetId}" class="dojoFloatingPane">
+	<div dojoAttachPoint="titleBar" class="dojoFloatingPaneTitleBar"  dojoAttachEvent="onMouseDown" style="display:none">
+	  	<img dojoAttachPoint="titleBarIcon"  class="dojoFloatingPaneTitleBarIcon">
+		<div dojoAttachPoint="closeAction" dojoAttachEvent="onClick:closeWindow"
+   	  		class="dojoFloatingPaneCloseIcon"></div>
+		<div dojoAttachPoint="restoreAction" dojoAttachEvent="onClick:restoreWindow"
+   	  		class="dojoFloatingPaneRestoreIcon"></div>
+		<div dojoAttachPoint="maximizeAction" dojoAttachEvent="onClick:maximizeWindow"
+   	  		class="dojoFloatingPaneMaximizeIcon"></div>
+		<div dojoAttachPoint="minimizeAction" dojoAttachEvent="onClick:minimizeWindow"
+   	  		class="dojoFloatingPaneMinimizeIcon"></div>
+	  	<div dojoAttachPoint="titleBarText" class="dojoFloatingPaneTitleText">${this.title}</div>
+	</div>
+
+	<div id="${this.widgetId}_container" dojoAttachPoint="containerNode" class="dojoFloatingPaneClient"></div>
+
+	<div dojoAttachPoint="resizeBar" class="dojoFloatingPaneResizebar" style="display:none"></div>
+</div>
\ No newline at end of file


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlFloatingPane.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlInlineEditBox.css
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HtmlInlineEditBox.css	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HtmlInlineEditBox.css	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,24 @@
+.editLabel {
+	font-size : small;
+	padding : 0 5px;
+	display : none;
+}
+
+.editableRegion {
+	background-color : #ffc !important;
+	cursor : pointer;
+	_cursor : hand;
+}
+
+.editableRegion .editLabel {
+	display : inline;
+}
+
+.editableTextareaRegion .editLabel {
+	display : block;
+}
+
+.inlineEditBox {
+	/*background-color : #ffc;*/
+	display : inline;
+}


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlInlineEditBox.css
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlInlineEditBox.html
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HtmlInlineEditBox.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HtmlInlineEditBox.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,6 @@
+<form class="inlineEditBox" style="display: none" dojoAttachPoint="form" dojoAttachEvent="onSubmit:saveEdit; onReset:cancelEdit; onKeyUp: checkForValueChange;">
+	<input type="text" dojoAttachPoint="text" style="display: none;" />
+	<textarea dojoAttachPoint="textarea" style="display: none;"></textarea>
+	<input type="submit" value="Save" dojoAttachPoint="submitButton" />
+	<input type="reset" value="Cancel" dojoAttachPoint="cancelButton" />
+</form>


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlInlineEditBox.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlMenu2.css
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HtmlMenu2.css	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HtmlMenu2.css	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,202 @@
+
+.dojoPopupMenu2 {
+	position: absolute;
+	border: 1px solid;
+	border-color: ThreeDLightShadow ThreeDDarkShadow ThreeDDarkShadow ThreeDLightShadow;
+}
+
+.dojoPopupMenu2Client {
+	border: 1px solid;
+	border-color: ThreeDHighlight ThreeDShadow ThreeDShadow ThreeDHighlight;
+	background-color: ThreeDFace;
+	padding: 1px;
+}
+
+.dojoMenuItem2 {
+	position: relative;
+	white-space: nowrap;
+	font: menu;
+	color: WindowText;
+	margin: 0;
+}
+
+.dojoMenuItem2 span {
+	margin: 0;
+}
+
+.dojoMenuItem2Hover {
+	background-color: Highlight;
+	color: HighlightText;
+}
+
+.dojoMenuItem2Icon {
+	position: absolute;
+	background-position: center center;
+	background-repeat: no-repeat;
+	z-index: 1;
+}
+
+.dojoMenuItem2Label {
+	position: absolute;
+	vertical-align: middle;
+	z-index: 1;
+}
+
+.dojoMenuItem2Label span {
+	position: relative;
+	vertical-align: middle;
+	z-index: 2;
+}
+
+.dojoMenuItem2Label span span {
+	position: absolute;
+	color: ThreeDHighlight;
+	display: none;
+	left: 1px;
+	top: 1px;
+	z-index: -2;
+}
+
+.dojoMenuItem2Accel {
+	position: absolute;
+	vertical-align: middle;
+	z-index: 1;
+}
+
+.dojoMenuItem2Accel span {
+	position: relative;
+	vertical-align: middle;
+	z-index: 2;
+}
+
+.dojoMenuItem2Accel span span {
+	position: absolute;
+	color: ThreeDHighlight;
+	display: none;
+	left: 1px;
+	top: 1px;
+	z-index: -2;
+}
+
+.dojoMenuItem2Disabled .dojoMenuItem2Label span,
+.dojoMenuItem2Disabled .dojoMenuItem2Accel span {
+	color: ThreeDShadow;
+}
+
+.dojoMenuItem2Disabled .dojoMenuItem2Label span span,
+.dojoMenuItem2Disabled .dojoMenuItem2Accel span span {
+	color: ThreeDHighlight;
+	display: block;
+}
+
+.dojoMenuItem2Hover .dojoMenuItem2Label span span,
+.dojoMenuItem2Hover .dojoMenuItem2Accel span span {
+	display: none;
+}
+
+.dojoMenuItem2Submenu {
+	position: absolute;
+	background-position: center center;
+	background-repeat: no-repeat;
+}
+
+.dojoMenuItem2Target {
+	position: absolute;
+	z-index: 10;
+	font-size: 1px;
+	background-image: url('images/transparent.gif');
+}
+
+.dojoMenuSeparator2 {
+	font-size: 1px;
+	margin: 0;
+}
+
+.dojoMenuSeparator2Top {
+	height: 50%;
+	border-bottom: 1px solid ThreeDShadow;
+	margin: 0px 2px;
+	font-size: 1px;
+}
+
+.dojoMenuSeparator2Bottom {
+	height: 50%;
+	border-top: 1px solid ThreeDHighlight;
+	margin: 0px 2px;
+	font-size: 1px;
+}
+
+
+
+.dojoMenuBar2 {
+	/*position: relative;*/
+	background-color: ThreeDFace;
+	border-bottom: 1px solid ThreeDHighlight;
+}
+
+.dojoMenuBar2Client {
+	padding: 1px;
+	border-top: 1px solid ThreeDHighlight;
+	border-bottom: 1px solid ThreeDShadow;
+}
+
+.dojoMenuBarItem2 {
+	position: absolute;
+	white-space: nowrap;
+	font: menu;
+	color: WindowText;
+	margin: 0;
+}
+
+.dojoMenuBarItem2 span {
+	margin: 0;
+}
+
+.dojoMenuBarItem2Target {
+	position: absolute;
+	z-index: 10;
+	font-size: 1px;
+	background-image: url('images/transparent.gif');
+}
+
+.dojoMenuBarItem2Label {
+	position: absolute;
+	vertical-align: middle;
+	z-index: 1;
+	padding: 3px 8px;
+}
+
+.dojoMenuBarItem2Label span {
+	position: relative;
+	z-index: 2;
+}
+
+.dojoMenuBarItem2Label span span {
+	position: absolute;
+	color: ThreeDHighlight;
+	display: none;
+	left: 1px;
+	top: 1px;
+	z-index: -2;
+}
+
+.dojoMenuBarItem2Hover {
+	background-color: Highlight;
+	color: HighlightText;
+}
+
+.dojoMenuBarItem2Disabled .dojoMenuBarItem2Label span,
+.dojoMenuBarItem2Disabled .dojoMenuBarItem2Accel span {
+	color: ThreeDShadow;
+}
+
+.dojoMenuBarItem2Disabled .dojoMenuBarItem2Label span span,
+.dojoMenuBarItem2Disabled .dojoMenuBarItem2Accel span span {
+	color: ThreeDHighlight;
+	display: block;
+}
+
+.dojoMenuBarItem2Hover .dojoMenuBarItem2Label span span,
+.dojoMenuBarItem2Hover .dojoMenuBarItem2Accel span span {
+	display: none;
+}


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlMenu2.css
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlMenuItemTemplate.html
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HtmlMenuItemTemplate.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HtmlMenuItemTemplate.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,2 @@
+<div dojoAttachPoint="labelNode" dojoAttachEvent="onClick">
+</div>


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlMenuItemTemplate.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlMonthlyCalendar.css
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HtmlMonthlyCalendar.css	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HtmlMonthlyCalendar.css	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,91 @@
+.datePickerContainer {
+	margin:0.5em 2em 0.5em 0;
+	/*width:10em;*/
+	float:left;
+}
+
+.previousMonth {
+	background-color:#bbbbbb;
+}
+
+.currentMonth {
+	background-color:#8f8f8f;
+}
+
+.nextMonth {
+	background-color:#eeeeee;
+}
+
+.currentDate {
+	text-decoration:underline;
+	font-style:italic;
+}
+
+.selectedItem {
+	background-color:#3a3a3a;
+	color:#ffffff;
+}
+
+.calendarContainer {
+	border-collapse:collapse;
+	border-spacing:0;
+	border-bottom:1px solid #e6e6e6;
+	overflow: hidden;
+	text-align: right;
+}
+
+.calendarContainer thead{
+	border-bottom:1px solid #e6e6e6;
+}
+
+.calendarContainer tbody * td {
+        height: 100px;
+        border: 1px solid gray;
+}
+
+.calendarContainer td {
+        width: 100px;
+        padding: 2px;
+	vertical-align: top;
+}
+
+.monthLabel {
+	font-size:0.9em;
+	font-weight:400;
+	margin:0;
+	text-align:center;
+}
+
+.monthLabel .month {
+	padding:0 0.4em 0 0.4em;
+}
+
+.yearLabel {
+	font-size:0.9em;
+	font-weight:400;
+	margin:0.25em 0 0 0;
+	text-align:right;
+	color:#a3a3a3;
+}
+
+.yearLabel .selectedYear {
+	color:#000;
+	padding:0 0.2em;
+}
+
+.nextYear, .previousYear {
+	cursor:pointer;cursor:hand;
+}
+
+.incrementControl {
+	cursor:pointer;cursor:hand;
+	width:1em;
+}
+
+.dojoMonthlyCalendarEvent {
+	font-size:0.7em;
+	overflow: hidden;
+	font-color: grey;
+	white-space: nowrap;
+	text-align: left;
+}


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlMonthlyCalendar.css
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlMonthlyCalendar.html
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HtmlMonthlyCalendar.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HtmlMonthlyCalendar.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,110 @@
+<div class="datePickerContainer" dojoAttachPoint="datePickerContainerNode">
+	<h3 class="monthLabel">
+	<!--
+	<span 
+		dojoAttachPoint="decreaseWeekNode" 
+		dojoAttachEvent="onClick: onIncrementWeek;" 
+		class="incrementControl">
+		<img src="${dojoRoot}src/widget/templates/decrementWeek.gif" alt="&uarr;" />
+	</span>
+	-->
+	<span 
+		dojoAttachPoint="decreaseMonthNode" 
+		dojoAttachEvent="onClick: onIncrementMonth;" class="incrementControl">
+		<img src="${dojoRoot}src/widget/templates/decrementMonth.gif" 
+			alt="&uarr;" dojoAttachPoint="decrementMonthImageNode">
+	</span>
+	<span dojoAttachPoint="monthLabelNode" class="month">July</span>
+	<span 
+		dojoAttachPoint="increaseMonthNode" 
+		dojoAttachEvent="onClick: onIncrementMonth;" class="incrementControl">
+		<img src="${dojoRoot}src/widget/templates/incrementMonth.gif" 
+			alt="&darr;"  dojoAttachPoint="incrementMonthImageNode">
+	</span>
+	<!--
+		<span dojoAttachPoint="increaseWeekNode" 
+			dojoAttachEvent="onClick: onIncrementWeek;" 
+			class="incrementControl">
+			<img src="${dojoRoot}src/widget/templates/incrementWeek.gif" 
+			alt="&darr;" />
+		</span>
+	-->
+	</h3>
+	<table class="calendarContainer">
+		<thead>
+			<tr>
+				<td>Sunday</td>
+				<td>Monday</td>
+				<td>Tuesday</td>
+				<td>Wednesday</td>
+				<td>Thursday</td>
+				<td>Friday</td>
+				<td>Saturday</td>
+			</tr>
+		</thead>
+		<tbody dojoAttachPoint="calendarDatesContainerNode" 
+			dojoAttachEvent="onClick: onSetDate;">
+			<tr dojoAttachPoint="calendarRow0">
+				<td></td>
+				<td></td>
+				<td></td>
+				<td></td>
+				<td></td>
+				<td></td>
+				<td></td>
+			</tr>
+			<tr dojoAttachPoint="calendarRow1">
+				<td></td>
+				<td></td>
+				<td></td>
+				<td></td>
+				<td></td>
+				<td></td>
+				<td></td>
+			</tr>
+			<tr dojoAttachPoint="calendarRow2">
+				<td></td>
+				<td></td>
+				<td></td>
+				<td></td>
+				<td></td>
+				<td></td>
+				<td></td>
+			</tr>
+			<tr dojoAttachPoint="calendarRow3">
+				<td></td>
+				<td></td>
+				<td></td>
+				<td></td>
+				<td></td>
+				<td></td>
+				<td></td>
+			</tr>
+			<tr dojoAttachPoint="calendarRow4">
+				<td></td>
+				<td></td>
+				<td></td>
+				<td></td>
+				<td></td>
+				<td></td>
+				<td></td>
+			</tr>
+			<tr dojoAttachPoint="calendarRow5">
+				<td></td>
+				<td></td>
+				<td></td>
+				<td></td>
+				<td></td>
+				<td></td>
+				<td></td>
+			</tr>
+		</tbody>
+	</table>
+	<h3 class="yearLabel">
+		<span dojoAttachPoint="previousYearLabelNode"
+			dojoAttachEvent="onClick: onIncrementYear;" class="previousYear"></span>
+		<span class="selectedYear" dojoAttachPoint="currentYearLabelNode"></span>
+		<span dojoAttachPoint="nextYearLabelNode" 
+			dojoAttachEvent="onClick: onIncrementYear;" class="nextYear"></span>
+	</h3>
+</div>


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlMonthlyCalendar.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlResizableTextarea.css
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HtmlResizableTextarea.css	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HtmlResizableTextarea.css	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,15 @@
+div.statusBar {
+	background-color: ThreeDFace;
+	height: 28px;
+	padding: 1px;
+	overflow: hidden;
+	font-size: 12px;
+}
+
+div.statusPanel {
+	background-color: ThreeDFace;
+	border: 1px solid;
+	border-color: ThreeDShadow ThreeDHighlight ThreeDHighlight ThreeDShadow;
+	margin: 1px;
+	padding: 2px 6px;
+}


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlResizableTextarea.css
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlResizableTextarea.html
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HtmlResizableTextarea.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HtmlResizableTextarea.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,14 @@
+<div>
+	<div style="border: 2px solid black; width: 90%; height: 200px;"
+		dojoAttachPoint="rootLayoutNode">
+		<div dojoAttachPoint="textAreaContainerNode" 
+			style="border: 0px; margin: 0px; overflow: hidden;">
+		</div>
+		<div dojoAttachPoint="statusBarContainerNode" class="statusBar">
+			<div dojoAttachPoint="statusLabelNode" 
+				class="statusPanel"
+				style="padding-right: 0px; z-index: 1;">drag to resize</div>
+			<div dojoAttachPoint="resizeHandleNode"></div>
+		</div>
+	</div>
+</div>


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlResizableTextarea.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlResizeHandle.css
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HtmlResizeHandle.css	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HtmlResizeHandle.css	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,12 @@
+.dojoHtmlResizeHandle {
+	float: right;
+	position: absolute;
+	right: 2px;
+	bottom: 2px;
+	width: 13px;
+	height: 13px;
+	z-index: 20;
+	cursor: nw-resize;
+	background-image: url(grabCorner.gif);
+	line-height: 0px;
+}
\ No newline at end of file


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlResizeHandle.css
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlShow.css
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HtmlShow.css	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HtmlShow.css	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,59 @@
+ at media screen {
+	html, body {
+		margin: 0px;
+		padding: 0px;
+		width: 100%;
+	}
+	body {
+		display: none;
+	}
+	h1 {
+		font-size: 50px;
+	}
+	p, li {
+		font-size: 30px;
+	}
+	.dojoShowNav {
+		background: #369;
+		overflow: hidden;
+		position: absolute;
+		height: 5px;
+		bottom: 0px;
+		left: 0px;
+		width: 100%;
+		text-align: center;
+	}
+	.dojoShowNav input {
+		margin: 0px;
+	}
+	.dojoShowHider {
+		height: 5px;
+		overflow: hidden;
+		width: 100%;
+	}
+	.dojoShowPrint {
+		position: absolute;
+		left: 5px;
+		top: 0px;
+	}
+}
+ at media print {
+	.dojoShow {
+		display: none !important;
+	}
+	.dojoShowPrint {
+		display: block !important;
+	}
+	.dojoShowPrintSlide {
+		border: 1px solid #aaa;
+		padding: 10px;
+		margin-bottom: 15px;
+	}
+	.dojoShowPrintSlide, ul {
+	page-break-inside: avoid;
+	}
+	h1 {
+		margin-top: 0;
+		page-break-after: avoid;
+	}
+}
\ No newline at end of file


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlShow.css
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlShow.html
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HtmlShow.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HtmlShow.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,11 @@
+<div class="dojoShow">
+	<div dojoAttachPoint="contentNode"></div>
+	<div class="dojoShowNav" dojoAttachPoint="nav">
+		<div class="dojoShowHider" dojoAttachPoint="hider"></div>
+		<span dojoAttachEvent="onClick:previousSlide">&lt;</span>
+		<select dojoAttachEvent="onClick:gotoSlideByEvent" dojoAttachPoint="select">
+			<option dojoAttachPoint="option">Title</option>
+		</select>
+		<span dojoAttachEvent="onClick:nextSlide">&gt;</span>
+	</div>
+</div>
\ No newline at end of file


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlShow.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlShowSlide.css
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HtmlShowSlide.css	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HtmlShowSlide.css	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,12 @@
+.dojoShowSlideTitle {
+	height: 100px;
+	background: #369;
+}
+.dojoShowSlideTitle h1 {
+	margin-top: 0;
+	line-height: 100px;
+	margin-left: 30px;
+}
+.dojoShowSlideBody {
+	margin: 15px;
+}
\ No newline at end of file


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlShowSlide.css
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlShowSlide.html
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HtmlShowSlide.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HtmlShowSlide.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,6 @@
+<div class="dojoShowSlide">
+	<div class="dojoShowSlideTitle">
+		<h1 dojoAttachPoint="htmlTitle">Title</h1>
+	</div>
+	<div class="dojoShowSlideBody" dojoAttachPoint="containerNode"></div>
+</div>
\ No newline at end of file


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlShowSlide.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlSimpleDropdownButtons.css
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HtmlSimpleDropdownButtons.css	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HtmlSimpleDropdownButtons.css	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,100 @@
+ul.dojoSimpleDropdownButtons {
+	margin : 0;
+	padding : 5px 0;
+}
+
+ul.dojoSimpleDropdownButtons li {
+	display : inline;
+	margin-right : 5px;
+	padding : 2px 0;
+}
+
+ul.dojoSimpleDropdownButtons li a {
+	padding : 2px 9px;
+	border : 2px outset #ccc;
+	border-right-width : 1px;
+	background : #f4f4f4;
+	color : #333;
+	text-decoration : none;
+}
+
+ul.dojoSimpleDropdownButtons li ul {
+	display : none;
+}
+
+ul.dojoSimpleDropdownButtons li a.disabled {
+	color : #999;
+	cursor : default;
+}
+
+ul.dojoSimpleDropdownButtons li .downArrow {
+	display : inline;
+	padding : 2px 4px;
+	border : 2px outset #ccc;
+	border-left : 0;
+	background : #f4f4f4 url(images/dropdownButtonsArrow.gif) no-repeat 4px 9px;
+	text-decoration : none;
+	color : black;
+	cursor : pointer;
+	_cursor : hand;
+}
+
+ul.dojoSimpleDropdownButtons li .downArrow.disabled {
+	background-image : url(images/dropdownButtonsArrow-disabled.gif);
+	cursor : default;
+}
+
+ul.dojoSimpleDropdownButtons li a:hover,
+ul.dojoSimpleDropdownButtons li span.downArrow:hover {
+	color : black;
+	background-color : #ddd;
+}
+
+ul.dojoSimpleDropdownButtons li .downArrow.pressed, ul.dojoSimpleDropdownButtons li .downArrow:focus {
+	border-style : inset;
+	background-position : 5px 10px;
+	padding : 2px 4px;
+}
+
+ul.dojoSimpleDropdownButtons li a.disabled:hover,
+ul.dojoSimpleDropdownButtons li span.downArrow.disabled:hover {
+	color : #999;
+	background-color : #f4f4f4;
+}
+
+ul.dojoSimpleDropdownButtons li a:focus {
+	padding : 3px 8px 1px 10px;
+	color : #333;
+	border-style : inset;
+}
+
+/* Menu
+ ******************** */
+ul.dojoSimpleDropdownButtonsMenu {
+	position : absolute;
+	margin : 0;
+	_margin : -2px;
+	padding : 0;
+	display : none;
+	border : 1px solid #aaa;
+	background : #f4f4f4;
+	list-style : none;
+	z-index : 99;
+}
+
+ul.dojoSimpleDropdownButtonsMenu li {
+	_display : inline;
+}
+
+ul.dojoSimpleDropdownButtonsMenu a {
+	display : block;
+	padding : 2px 5px;
+	color : #333;
+	text-decoration : none;
+}
+
+ul.dojoSimpleDropdownButtonsMenu a:hover {
+	background : #ddd;
+	color : black;
+}
+


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlSimpleDropdownButtons.css
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlSlideShow.css
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HtmlSlideShow.css	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HtmlSlideShow.css	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,9 @@
+.slideShowImg {
+	position: absolute;
+	left: 0px;
+	top: 0px; 
+	border: 2px solid #4d4d4d;
+	padding: 0px;
+	margin: 0px;
+}
+


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlSlideShow.css
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlSlideShow.html
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HtmlSlideShow.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HtmlSlideShow.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,15 @@
+<div style="position: relative; padding: 3px;">
+		<div dojoAttachPoint="controlsContainer">
+			<input type="button" value="pause" 
+				dojoAttachPoint="startStopButton"
+				dojoAttachEvent="onClick: togglePaused;">
+		</div>
+		<div style="position: relative; width: 800px; height: 600px;"
+			dojoAttachPoint="imagesContainer"
+			dojoAttachEvent="onClick: togglePaused;">
+			<img dojoAttachPoint="img1" class="slideShowImg" 
+				style="z-index: 1;"  />
+			<img dojoAttachPoint="img2" class="slideShowImg" 
+				style="z-index: 0;" />
+		</div>	
+</div>
\ No newline at end of file


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlSlideShow.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlSlider.css
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HtmlSlider.css	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HtmlSlider.css	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,47 @@
+.sliderMain { 
+  position: relative; 
+  /*top: 80px; left: 0px;*/
+  padding: 0px; 
+  border: 1px solid black;
+  width: 300px; height: 300px; 
+}
+
+.sliderHandle { 
+  color: #FF0000;
+  position: relative; 
+  background-image: url(images/slider-button.png);
+  width: 25px; 
+  height: 25px;
+}
+
+.sliderMainHorizontal { 
+  position: relative; 
+  /*top: 80px; left: 0px;*/
+  padding: 0px; 
+  width: 300px; height: 35px; 
+  background: url(images/slider-bg.gif) no-repeat 0px 4px;
+}
+
+.sliderHandleHorizontal { 
+  color: #FF0000;
+  position: relative; 
+  background-image: url(images/slider-button-horz.png);
+  width: 25px; 
+  height: 35px;
+}
+
+.sliderMainVertical { 
+  position: relative; 
+  /*top: 80px; left: 0px;*/
+  padding: 0px; 
+  width: 35px; height: 300px; 
+  background: url(images/slider-bg-vert.gif) no-repeat 4px 0px;
+}
+
+.sliderHandleVertical { 
+  color: #FF0000;
+  position: relative; 
+  background-image: url(images/slider-button-vert.png);
+  width: 35px; 
+  height: 25px;
+}
\ No newline at end of file


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlSlider.css
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlSlider.html
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HtmlSlider.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HtmlSlider.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,3 @@
+<div class="sliderMain">
+    <div class="sliderHandle" dojoAttachPoint="sliderHandle"></div>
+</div>


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlSlider.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlSliderHorizontal.html
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HtmlSliderHorizontal.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HtmlSliderHorizontal.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,3 @@
+<div class="sliderMainHorizontal">
+    <div class="sliderHandleHorizontal" dojoAttachPoint="sliderHandle"></div>
+</div>


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlSliderHorizontal.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlSliderVertical.html
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HtmlSliderVertical.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HtmlSliderVertical.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,3 @@
+<div class="sliderMainVertical">
+    <div class="sliderHandleVertical" dojoAttachPoint="sliderHandle"></div>
+</div>


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlSliderVertical.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlSpinner.css
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HtmlSpinner.css	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HtmlSpinner.css	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,39 @@
+input.dojoSpinnerInput {
+	/* font-size: 0.8em; */
+	border: 0px;
+}
+
+.dojoSpinnerOptions {
+	font-family: Verdana, Helvetica, Garamond, sans-serif;
+	/* font-size: 0.7em; */
+	background-color: white;
+	border: 1px solid #afafaf;
+	position: absolute;
+	z-index: 1000; 
+	overflow: auto;
+	-moz-opacity: 0;
+	cursor: default;
+}
+
+table.dojoSpinner {
+	border: 1px solid #afafaf;
+}
+
+.dojoSpinnerItem {
+	padding-left: 2px;
+	padding-top: 2px;
+	margin: 0px;
+}
+
+.dojoSpinnerItemEven {
+	background-color: #f4f4f4;
+}
+
+.dojoSpinnerItemOdd {
+	background-color: white;
+}
+
+.dojoSpinnerItemHighlight {
+	background-color: #63709A;
+	color: white;
+}


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlSpinner.css
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlSpinner.html
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HtmlSpinner.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HtmlSpinner.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,28 @@
+<span style="display:none;">
+	<span style="position:relative;" dojoAttachPoint="relNode">
+		<span style="position:absolute; left:0; top:0;" dojoAttachPoint="absNode">
+			<span style="position:relative;" dojoAttachPoint="innerRelNode">
+				<img border="0"
+					hspace="0"
+					vspace="0"
+					style="position:absolute; left:0;top:0; border-right:1px solid black;border-bottom:1px solid black;"
+					dojoAttachPoint="upArrowNode"
+					src="${dojoRoot}src/widget/templates/images/spinnerIncrement.gif">
+				<img border="0"
+					hspace="0"
+					vspace="0"
+					style="vertical-align:bottom;border-right:1px solid black;border-bottom:1px solid black;"
+					dojoAttachPoint="downArrowNode"
+					src="${dojoRoot}src/widget/templates/images/spinnerDecrement.gif">
+			</span>
+		</span>
+		<span style="position:relative;" dojoAttachPoint="spacerRelNode">
+			<img border="0"
+				hspace="0"
+				vspace="0"
+				style="visibility:hidden;"
+				dojoAttachPoint="spacerNode"
+				src="${dojoRoot}src/widget/templates/images/spinnerIncrement.gif">
+		</span>
+	</span>
+</span>


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlSpinner.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlSplitContainer.css
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HtmlSplitContainer.css	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HtmlSplitContainer.css	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,39 @@
+.dojoSplitContainer{
+	position: relative;
+	overflow: hidden;
+}
+
+.dojoSplitPane{
+	position: absolute;
+}
+
+.dojoSplitContainerSizerH,
+.dojoSplitContainerSizerV {
+	font-size: 1px;
+	cursor: move;
+	cursor: w-resize;
+	background-color: ThreeDFace;
+	border: 1px solid;
+	border-color: ThreeDHighlight ThreeDShadow ThreeDShadow ThreeDHighlight;
+	margin: 0;
+}
+
+.dojoSplitContainerSizerV {
+	cursor: n-resize;
+}
+
+.dojoSplitContainerVirtualSizerH,
+.dojoSplitContainerVirtualSizerV {
+	font-size: 1px;
+	cursor: move;
+	cursor: w-resize;
+	background-color: ThreeDShadow;
+	-moz-opacity: 0.5;
+	opacity: 0.5;
+	filter: Alpha(Opacity=50);
+	margin: 0;
+}
+
+.dojoSplitContainerVirtualSizerV {
+	cursor: n-resize;
+}


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlSplitContainer.css
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlTabContainer.css
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HtmlTabContainer.css	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HtmlTabContainer.css	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,173 @@
+.dojoTabContainer {
+	position : relative;
+}
+
+.dojoTabPaneWrapper {
+	position : relative;
+	border : 1px solid #6290d2;
+	clear: both;
+}
+
+.dojoTabLabels-top {
+	position : absolute;
+	top : 0px;
+	left : 0px;
+	overflow : visible;
+	margin-bottom : -1px;
+	width : 100%;
+	z-index: 2;	/* so the bottom of the tab label will cover up the border of dojoTabPaneWrapper */
+}
+.dojoTabLabels-top-noLayout {
+	overflow : visible;
+	margin-bottom : -1px;
+	width : 100%;
+	z-index: 2;
+}
+
+.dojoTabPaneTab {
+	position : relative;
+	float : left;
+	padding-left : 9px;
+	border-bottom : 1px solid #6290d2;
+	background : url(images/tab_left.gif) no-repeat left top;
+	cursor: pointer;
+}
+
+.dojoTabPaneTab span {
+	display : block;
+	padding : 4px 15px 4px 6px;
+	background : url(images/tab_top_right.gif) no-repeat right top;
+	color : #333;
+	font-size : 90%;
+}
+
+.dojoTabPanePaneClose {
+	position : absolute;
+	bottom : 0px;
+	right : 6px;
+	height : 12px;
+	width : 12px;
+	background : url(images/tab_close.gif) no-repeat right top;
+}
+
+.dojoTabPanePaneCloseHover {
+	background-image : url(images/tab_close_h.gif);
+}
+
+.dojoTabPaneTabClose {
+	display : inline;
+	height : 12px;
+	width : 12px;
+	padding : 0 12px 0 0;
+	margin : 0 -10px 0 10px;
+	background : url(images/tab_close.gif) no-repeat right top;
+	cursor : default;
+}
+
+.dojoTabPaneTabCloseHover {
+	background-image : url(images/tab_close_h.gif);
+}
+
+.dojoTabPaneTab.current {
+	padding-bottom : 1px;
+	border-bottom : 0;
+	background-position : 0 -150px;
+}
+
+.dojoTabPaneTab.current span {
+	padding-bottom : 5px;
+	margin-bottom : -1px;
+	background-position : 100% -150px;
+}
+
+/* bottom tabs */
+
+.dojoTabLabels-bottom {
+	position : absolute;
+	bottom : 0px;
+	left : 0px;
+	overflow : visible;
+	margin-top : -1px;
+	width : 100%;
+	z-index: 2;
+}
+
+.dojoTabLabels-bottom .dojoTabPaneTab {
+	border-top :  1px solid #6290d2;
+	border-bottom : 0;
+	background : url(images/tab_bot_left.gif) no-repeat left bottom;
+}
+
+.dojoTabLabels-bottom .dojoTabPaneTab span {
+	background : url(images/tab_bot_right.gif) no-repeat right bottom;
+}
+
+.dojoTabLabels-bottom .dojoTabPaneTab.current {
+	padding-top : 1px;
+	border-top : 0;
+	background : url(images/tab_bot_left_curr.gif) no-repeat left bottom;
+}
+
+.dojoTabLabels-bottom .dojoTabPaneTab.current span {
+	padding-top : 5px;
+	margin-top : -1px;
+	background : url(images/tab_bot_right_curr.gif) no-repeat right bottom;
+}
+
+/* right-h tabs */
+
+.dojoTabLabels-right-h {
+	position : absolute;
+	top : 0px;
+	right : 0px;
+	overflow : visible;
+	margin-left : -1px;
+	z-index: 2;
+}
+
+.dojoTabLabels-right-h .dojoTabPaneTab {
+	padding-left : 0;
+	border-left :  1px solid #6290d2;
+	border-bottom : 0;
+	background : url(images/tab_bot_right.gif) no-repeat right bottom;
+	float : none;
+}
+
+.dojoTabLabels-right-h .dojoTabPaneTab span {
+	padding : 4px 15px 4px 15px;
+}
+
+.dojoTabLabels-right-h .dojoTabPaneTab.current {
+	border-left :  0;
+	border-bottom :  1px solid #6290d2;
+}
+
+/* left-h tabs */
+
+.dojoTabLabels-left-h {
+	position : absolute;
+	top : 0px;
+	left : 0px;
+	overflow : visible;
+	margin-right : -1px;
+	z-index: 2;
+}
+
+.dojoTabLabels-left-h .dojoTabPaneTab {
+	border-right :  1px solid #6290d2;
+	border-bottom : 0;
+	float : none;
+	background : url(images/tab_top_left.gif) no-repeat left top;
+}
+
+.dojoTabLabels-left-h .dojoTabPaneTab.current {
+	border-right : 0;
+	border-bottom :  1px solid #6290d2;
+	padding-bottom : 0;
+	background : url(images/tab_top_left.gif) no-repeat 0 -150px;
+}
+
+.dojoTabLabels-left-h .dojoTabPaneTab span {
+	background : 0;
+	border-bottom :  1px solid #6290d2;
+}


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlTabContainer.css
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlTabContainer.html
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HtmlTabContainer.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HtmlTabContainer.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,4 @@
+<div id="${this.widgetId}" class="dojoTabContainer" waiRole="tabpanel">
+	<div dojoAttachPoint="dojoTabLabels"></div>
+	<div class="dojoTabPaneWrapper" dojoAttachPoint="containerNode" ></div>
+</div>


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlTabContainer.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlTaskBar.css
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HtmlTaskBar.css	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HtmlTaskBar.css	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,29 @@
+.dojoTaskBarItem {
+	display: inline-block;
+	background-color: ThreeDFace;
+	border: outset 2px;
+	margin-right: 5px;
+	cursor: pointer;
+	height: 35px;
+	width: 100px;
+	font-size: 10pt;
+	white-space: nowrap;
+	text-align: center;
+	float: left;
+	overflow: hidden;
+}
+
+.dojoTaskBarItem img {
+	vertical-align: middle;
+	margin-right: 5px;
+	margin-left: 5px;	
+	height: 32px;
+	width: 32px;
+}
+
+.dojoTaskBarItem a {
+	 color: black;
+	text-decoration: none;
+}
+
+


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlTaskBar.css
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlTaskBarItemTemplate.html
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HtmlTaskBarItemTemplate.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HtmlTaskBarItemTemplate.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,2 @@
+<div class="dojoTaskBarItem" dojoAttachEvent="onClick">
+</div>
\ No newline at end of file


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlTaskBarItemTemplate.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlTimePicker.css
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HtmlTimePicker.css	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HtmlTimePicker.css	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,37 @@
+.timePickerContainer {
+	margin:1.75em 0 0.5em 0;
+	width:10em;
+	float:left;
+}
+
+.timeContainer {
+	border-collapse:collapse;
+	border-spacing:0;
+}
+
+.timeContainer thead td{
+	border-bottom:1px solid #e6e6e6;
+	padding:0 0.4em 0.2em 0.4em;
+}
+
+.timeContainer td {
+	font-size:0.9em;
+	padding:0 0.25em 0 0.25em;
+	text-align:left;
+	cursor:pointer;cursor:hand;
+}
+
+.timeContainer td.minutesHeading {
+	border-left:1px solid #e6e6e6;
+	border-right:1px solid #e6e6e6;	
+}
+
+.timeContainer .minutes {
+	border-left:1px solid #e6e6e6;
+	border-right:1px solid #e6e6e6;
+}
+
+.selectedItem {
+	background-color:#3a3a3a;
+	color:#ffffff;
+}


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlTimePicker.css
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlTimePicker.html
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HtmlTimePicker.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HtmlTimePicker.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,99 @@
+<div class="timePickerContainer" dojoAttachPoint="timePickerContainerNode">
+	<table class="timeContainer" cellspacing="0" >
+		<thead>
+			<tr>
+				<td>Hour</td>
+				<td class="minutesHeading">Minute</td>
+				<td>&nbsp;</td>
+			</tr>
+		</thead>
+		<tbody>
+			<tr>
+				<td valign="top">
+					<table>
+						<tbody dojoAttachPoint="hourContainerNode"  
+							dojoAttachEvent="onClick: onSetSelectedHour;">
+							<tr>
+								<td>12</td>
+								<td>6</td>
+							</tr>
+							<tr>
+								<td>1</td>
+								<td>7</td>
+							</tr>
+							<tr>
+								<td>2</td>
+								<td>8</td>
+							</tr>
+							<tr>
+								<td>3</td>
+								<td>9</td>
+							</tr>
+							<tr>
+								<td>4</td>
+								<td>10</td>
+							</tr>
+							<tr>
+								<td>5</td>
+								<td>11</td>
+							</tr>
+						</tbody>
+					</table>
+				</td>
+				<td valign="top" class="minutes">
+					<table>
+						<tbody dojoAttachPoint="minuteContainerNode" 
+							dojoAttachEvent="onClick: onSetSelectedMinute;">
+							<tr>
+								<td>00</td>
+								<td>30</td>
+							</tr>
+							<tr>
+								<td>05</td>
+								<td>35</td>
+							</tr>
+							<tr>
+								<td>10</td>
+								<td>40</td>
+							</tr>
+							<tr>
+								<td>15</td>
+								<td>45</td>
+							</tr>
+							<tr>
+								<td>20</td>
+								<td>50</td>
+							</tr>
+							<tr>
+								<td>25</td>
+								<td>55</td>
+							</tr>
+						</tbody>
+					</table>
+				</td>
+				<td valign="top">
+					<table>
+						<tbody dojoAttachPoint="amPmContainerNode" 
+							dojoAttachEvent="onClick: onSetSelectedAmPm;">
+							<tr>
+								<td>AM</td>
+							</tr>
+							<tr>
+								<td>PM</td>
+							</tr>
+						</tbody>
+					</table>
+				</td>
+			</tr>
+			<tr>
+				<td></td>
+				<td>
+					<div dojoAttachPoint="anyTimeContainerNode" 
+						dojoAttachEvent="onClick: onSetSelectedAnyTime;" 
+						class="anyTimeContainer">any</div>
+				</td>
+				<td></td>
+			</tr>
+		</tbody>
+	</table>
+</div>


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlTimePicker.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlToolbar.css
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HtmlToolbar.css	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HtmlToolbar.css	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,54 @@
+.toolbarContainer {
+	border-bottom : 0;
+	background-color : #def;
+	color : ButtonText;
+	font : Menu;
+	background-image: url(images/toolbar-bg.gif);
+}
+
+.toolbar {
+	padding : 2px 4px;
+	min-height : 26px;
+	_height : 26px;
+}
+
+.toolbarItem {
+	float : left;
+	padding : 1px 2px;
+	margin : 0 2px 1px 0;
+	cursor : pointer;
+}
+
+.toolbarItem.selected, .toolbarItem.down {
+	margin : 1px 1px 0 1px;
+	padding : 0px 1px;
+	border : 1px solid #bbf;
+	background-color : #fafaff;
+}
+
+.toolbarButton img {
+	vertical-align : bottom;
+}
+
+.toolbarButton span {
+	line-height : 16px;
+	vertical-align : middle;
+}
+
+.toolbarButton.hover {
+	padding : 0px 1px;
+	border : 1px solid #99c;
+}
+
+.toolbarItem.disabled {
+	opacity : 0.3;
+	filter : alpha(opacity=30);
+	cursor : default;
+}
+
+.toolbarSeparator {
+	cursor : default;
+}
+
+.toolbarFlexibleSpace {
+}


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlToolbar.css
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlTooltipTemplate.css
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HtmlTooltipTemplate.css	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HtmlTooltipTemplate.css	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,9 @@
+.dojoTooltip {
+	border: solid black 1px;
+	background: beige;
+	color: black;
+	position: absolute;
+	font-size: small;
+	padding: 2px 2px 2px 2px;
+	z-index: 10;
+}


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlTooltipTemplate.css
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlTooltipTemplate.html
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/HtmlTooltipTemplate.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/HtmlTooltipTemplate.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,2 @@
+<div class='dojoTooltip' style="display:none" dojoAttachPoint="containerNode">
+</div>
\ No newline at end of file


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/HtmlTooltipTemplate.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/Menu.css
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/Menu.css	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/Menu.css	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,60 @@
+.dojoMenu {
+	border:1px solid #000000;
+	list-style-type:none;
+	margin:0;
+	padding:0;
+	padding-bottom: 1px;
+	background-color:#f4f4f4;
+	font-size: 8pt;
+}
+
+.dojoMenuSeparator {
+	list-style-type:none;
+	margin:0;
+	padding:1px 0;
+	border-bottom:1px solid #000000;
+	line-height:1px;
+	height:1px;
+}
+
+li:hover.dojoMenuSeparator {
+	background-color:#e5e5e5;
+	cursor:default;
+}
+
+
+
+
+.dojoContextMenu {
+	position: absolute;
+	display: none;
+	border: 2px solid;
+	border-color: ThreeDHighlight ThreeDShadow ThreeDShadow ThreeDHighlight;
+	list-style-type: none;
+	margin: 0;
+	padding: 1px;
+	background-color: ThreeDFace;
+	font-size: 8pt;
+}
+
+.dojoMenuItem {
+	white-space: nowrap;
+	padding: 2px;
+	font: menu;
+	color: WindowText;
+}
+
+.dojoMenuItem a {
+	text-decoration: none;
+	color: WindowText;
+	font: inherit;
+}
+
+.dojoMenuItemHover {
+	padding: 2px;
+	background-color: blue;
+	cursor: pointer;
+	_cursor: hand;
+	background-color: Highlight;
+	color: HighlightText;
+}
\ No newline at end of file


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/Menu.css
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/PopUpButton.css
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/PopUpButton.css	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/PopUpButton.css	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,35 @@
+.PopUpButton {
+	padding : 2px 6px 2px 9px;
+	border : 1px outset #ccc;
+	background : #f4f4f4;
+	color : #333;
+	text-decoration : none;
+}
+
+.PopUpButton .downArrow {
+	margin-left: 0.5em;
+	margin-bottom: 2px;
+}
+
+.downArrow.disabled {
+	background-image : url(images/dropdownButtonsArrow-disabled.gif);
+	cursor : default;
+}
+
+ul.dropdownButtons li a:hover,
+ul.dropdownButtons li span.downArrow:hover {
+	color : black;
+	background-color : #ddd;
+}
+
+ul.dropdownButtons li .downArrow.pressed, ul.dropdownButtons li .downArrow:focus {
+	border-style : inset;
+	background-position : 5px 10px;
+	padding : 2px 4px;
+}
+
+ul.dropdownButtons li a.disabled:hover,
+ul.dropdownButtons li span.downArrow.disabled:hover {
+	color : #999;
+	background-color : #f4f4f4;
+}


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/PopUpButton.css
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/TitlePane.html
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/TitlePane.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/TitlePane.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,4 @@
+<div dojoAttachPoint="domNode">
+<div dojoAttachPoint="labelNode" dojoAttachEvent="onclick: onLabelClick"></div>
+<div dojoAttachPoint="containerNode"></div>
+</div>


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/TitlePane.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/Tree.css
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/Tree.css	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/Tree.css	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,29 @@
+.dojoTree {
+	font: caption;
+	font-size: 11px;
+	font-weight: normal;
+	overflow: auto;
+}
+
+.dojoTreeNodeLabel {
+	padding: 1px 2px;
+	color: WindowText;
+	cursor: default;
+}
+
+.dojoTreeNodeLabel:hover {
+	text-decoration: underline;
+}
+
+.dojoTreeNodeLabelSelected {
+	background-color: Highlight;
+	color: HighlightText;
+}
+
+.dojoTree div {
+	white-space: nowrap;
+}
+
+.dojoTree img {
+	vertical-align: middle;
+}


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/Tree.css
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/Wizard.css
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/Wizard.css	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/Wizard.css	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,72 @@
+.WizardContainer {
+	background: #EEEEEE;
+	border: #798EC5 1px solid;
+	padding: 2px;
+}
+
+.WizardTitle {
+	color: #003366;
+	padding: 8px 5px 15px 2px;
+	font-weight: bold;
+	font-size: x-small;
+	font-style: normal;
+	font-family: Verdana, Arial, Helvetica;
+	text-align: left;
+}
+
+.WizardText {
+	color: #000033;
+	font-weight: normal;
+	font-size: xx-small;
+	font-family: Verdana, Arial, Helvetica;
+	padding: 2 50; text-align: justify;
+}
+
+.WizardLightText {
+	color: #666666;
+	font-weight: normal;
+	font-size: xx-small;
+	font-family: verdana, arial, helvetica;
+	padding: 2px 50px;
+	text-align: justify;
+}
+
+.WizardButtonHolder {
+	text-align: right;
+	padding: 10px 5px;
+}
+
+.WizardButton {
+	color: #ffffff;
+	background: #798EC5;
+	font-size: xx-small;
+	font-family: verdana, arial, helvetica, sans-serif;
+	border-right: #000000 1px solid;
+	border-bottom: #000000 1px solid;
+	border-left: #666666 1px solid;
+	border-top: #666666 1px solid;
+	padding-right: 4px;
+	padding-left: 4px;
+	text-decoration: none; height: 18px;
+}
+
+.WizardButton:hover {
+	cursor: pointer;
+}
+
+.WizardButtonDisabled {
+	color: #eeeeee;
+	background-color: #999999;
+	font-size: xx-small;
+	FONT-FAMILY: verdana, arial, helvetica, sans-serif;
+	border-right: #000000 1px solid;
+	border-bottom: #000000 1px solid;
+	border-left: #798EC5 1px solid;
+	border-top: #798EC5 1px solid;
+	padding-right: 4px;
+	padding-left: 4px;
+	text-decoration: none;
+	height: 18px;
+}
+
+


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/Wizard.css
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/Wizard.html
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/Wizard.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/Wizard.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,11 @@
+<div class="WizardContainer" dojoAttachPoint="wizardNode">
+    <div class="WizardText" dojoAttachPoint="wizardPanelContainerNode">
+    </div>
+    <div class="WizardButtonHolder" dojoAttachPoint="wizardControlContainerNode">
+        <input class="WizardButton" type="button" dojoAttachPoint="previousButton"/>
+        <input class="WizardButton" type="button" dojoAttachPoint="nextButton"/>
+        <input class="WizardButton" type="button" dojoAttachPoint="cancelButton"/>
+        <input class="WizardButton" type="button" dojoAttachPoint="doneButton" style="display:none"/>
+    </div>
+</div>
+


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/Wizard.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/-.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/-.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/aggregate.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/aggregate.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/backcolor.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/backcolor.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/bg-fade.png
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/bg-fade.png
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/bold.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/bold.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/cancel.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/cancel.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/copy.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/copy.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/createlink.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/createlink.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/cut.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/cut.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/delete.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/delete.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/forecolor.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/forecolor.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/hilitecolor.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/hilitecolor.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/indent.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/indent.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/inserthorizontalrule.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/inserthorizontalrule.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/insertimage.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/insertimage.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/insertorderedlist.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/insertorderedlist.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/inserttable.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/inserttable.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/insertunorderedlist.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/insertunorderedlist.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/italic.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/italic.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/justifycenter.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/justifycenter.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/justifyfull.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/justifyfull.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/justifyleft.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/justifyleft.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/justifyright.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/justifyright.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/left_to_right.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/left_to_right.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/list_bullet_indent.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/list_bullet_indent.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/list_bullet_outdent.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/list_bullet_outdent.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/list_num_indent.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/list_num_indent.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/list_num_outdent.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/list_num_outdent.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/outdent.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/outdent.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/paste.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/paste.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/redo.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/redo.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/removeformat.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/removeformat.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/right_to_left.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/right_to_left.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/save.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/save.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/space.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/space.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/strikethrough.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/strikethrough.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/subscript.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/subscript.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/superscript.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/superscript.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/underline.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/underline.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/undo.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/undo.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/wikiword.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/buttons/wikiword.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/check_disabled_off.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/check_disabled_off.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/check_disabled_on.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/check_disabled_on.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/check_hover_off.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/check_hover_off.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/check_hover_on.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/check_hover_on.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/check_off.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/check_off.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/check_on.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/check_on.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/decrementMonth.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/decrementMonth.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/decrementWeek.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/decrementWeek.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/grabCorner.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/grabCorner.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/Tree.css
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/Tree.css	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/Tree.css	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,36 @@
+
+.dojoTree {
+	font: caption;
+	font-size: 11px;
+	font-weight: normal;
+	overflow: auto;
+}
+
+
+.dojoTreeNodeLabelTitle {
+	padding-left: 2px;
+	color: WindowText;
+}
+
+.dojoTreeNodeLabel {
+	cursor:hand;
+	cursor:pointer;
+}
+
+.dojoTreeNodeLabelTitle:hover {
+	text-decoration: underline;
+}
+
+.dojoTreeNodeLabelSelected {
+	background-color: Highlight;
+	color: HighlightText;
+}
+
+.dojoTree div {
+	white-space: nowrap;
+}
+
+.dojoTree img, .dojoTreeNodeLabel img {
+	vertical-align: middle;
+}
+


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/Tree.css
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/blank.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/blank.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/closed.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/closed.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/document.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/document.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/minus.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/minus.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/plus.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/plus.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/transparent.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/transparent.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/treenode_blank.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/treenode_blank.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/treenode_child.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/treenode_child.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/treenode_expand_minus.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/treenode_expand_minus.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/treenode_expand_plus.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/treenode_expand_plus.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/treenode_grid_c.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/treenode_grid_c.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/treenode_grid_l.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/treenode_grid_l.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/treenode_grid_p.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/treenode_grid_p.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/treenode_grid_t.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/treenode_grid_t.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/treenode_grid_v.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/treenode_grid_v.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/treenode_grid_x.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/treenode_grid_x.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/treenode_grid_y.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/treenode_grid_y.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/treenode_grid_z.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/treenode_grid_z.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/treenode_loading.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/treenode_loading.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/treenode_loading.jpg
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/Tree/treenode_loading.jpg
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/bdYearBg.1.gif
===================================================================


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/bdYearBg.1.gif
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/bdYearBg.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/bdYearBg.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/blank.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/blank.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/combo_box_arrow.png
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/combo_box_arrow.png
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/dateIcon.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/dateIcon.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/decrementMonth.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/decrementMonth.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/decrementMonth.png
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/decrementMonth.png
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/dpBg.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/dpBg.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/dpCurveBL.png
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/dpCurveBL.png
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/dpCurveBR.png
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/dpCurveBR.png
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/dpCurveTL.png
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/dpCurveTL.png
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/dpCurveTR.png
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/dpCurveTR.png
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/dpHorizLine.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/dpHorizLine.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/dpHorizLineFoot.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/dpHorizLineFoot.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/dpMonthBg.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/dpMonthBg.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/dpMonthBg.png
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/dpMonthBg.png
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/dpVertLine.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/dpVertLine.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/dpYearBg.png
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/dpYearBg.png
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/dropdownButtonsArrow-disabled.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/dropdownButtonsArrow-disabled.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/dropdownButtonsArrow.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/dropdownButtonsArrow.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/floatingPaneClose.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/floatingPaneClose.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/floatingPaneMaximize.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/floatingPaneMaximize.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/floatingPaneMinimize.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/floatingPaneMinimize.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/floatingPaneRestore.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/floatingPaneRestore.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/hue.png
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/hue.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/incrementMonth.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/incrementMonth.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/incrementMonth.png
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/incrementMonth.png
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/no.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/no.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/no.svg
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/images/no.svg	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/images/no.svg	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 12.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 51448)  -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
+	<!ENTITY ns_svg "http://www.w3.org/2000/svg">
+	<!ENTITY ns_xlink "http://www.w3.org/1999/xlink">
+]>
+<svg  version="1.1" id="Layer_1" xmlns="&ns_svg;" xmlns:xlink="&ns_xlink;" width="15.433" height="15.433"
+	 viewBox="0 0 15.433 15.433" overflow="visible" enable-background="new 0 0 15.433 15.433" xml:space="preserve">
+<circle fill="none" stroke="#D02026" stroke-width="2" cx="7.716" cy="7.716" r="6.716"/>
+<line fill="none" stroke="#D02026" stroke-width="2" x1="3.02" y1="12.352" x2="12.639" y2="2.732"/>
+</svg>


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/no.svg
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/scBackground.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/scBackground.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/slider-bg-vert.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/slider-bg-vert.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/slider-bg.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/slider-bg.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/slider-button-horz.png
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/slider-button-horz.png
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/slider-button-vert.png
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/slider-button-vert.png
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/slider-button.png
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/slider-button.png
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/soriaActive-c.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/soriaActive-c.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/soriaActive-l.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/soriaActive-l.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/soriaActive-r.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/soriaActive-r.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/soriaButton-c.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/soriaButton-c.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/soriaButton-l.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/soriaButton-l.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/soriaButton-r.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/soriaButton-r.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/soriaDisabled-c.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/soriaDisabled-c.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/soriaDisabled-l.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/soriaDisabled-l.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/soriaDisabled-r.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/soriaDisabled-r.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/soriaPressed-c.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/soriaPressed-c.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/soriaPressed-l.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/soriaPressed-l.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/soriaPressed-r.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/soriaPressed-r.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/spinnerDecrement.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/spinnerDecrement.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/spinnerIncrement.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/spinnerIncrement.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/submenu_off.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/submenu_off.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/submenu_on.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/submenu_on.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/tab_bot_left.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/tab_bot_left.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/tab_bot_left_curr.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/tab_bot_left_curr.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/tab_bot_right.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/tab_bot_right.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/tab_bot_right_curr.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/tab_bot_right_curr.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/tab_close.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/tab_close.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/tab_close_h.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/tab_close_h.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/tab_left.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/tab_left.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/tab_left_r.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/tab_left_r.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/tab_left_r_curr.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/tab_left_r_curr.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/tab_right.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/tab_right.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/tab_right_r.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/tab_right_r.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/tab_right_r_curr.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/tab_right_r_curr.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/tab_top_left.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/tab_top_left.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/tab_top_right.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/tab_top_right.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/toolbar-bg.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/toolbar-bg.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/transparent.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/transparent.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/treenode_blank.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/treenode_blank.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/treenode_child.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/treenode_child.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/treenode_expand_minus.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/treenode_expand_minus.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/treenode_expand_plus.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/treenode_expand_plus.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/treenode_grid_c.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/treenode_grid_c.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/treenode_grid_l.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/treenode_grid_l.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/treenode_grid_p.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/treenode_grid_p.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/treenode_grid_t.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/treenode_grid_t.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/treenode_grid_v.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/treenode_grid_v.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/treenode_grid_x.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/treenode_grid_x.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/treenode_grid_y.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/treenode_grid_y.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/treenode_grid_z.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/treenode_grid_z.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/treenode_node.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/treenode_node.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/verticalbar.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/verticalbar.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/images/whiteDownArrow.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/images/whiteDownArrow.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/incrementMonth.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/incrementMonth.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/incrementWeek.gif
===================================================================
(Binary files differ)


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/incrementWeek.gif
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: trunk/PhotoOp/root/static/js/src/widget/templates/richtextframe.html
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/templates/richtextframe.html	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/templates/richtextframe.html	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
+<html>
+  <head>
+    <title></title>
+    <script type="text/javascript">
+    function init(){
+       document.designMode = 'On';
+       parentPageDomain = document.location.href.split('#')[1];
+       if (parentPageDomain) {
+          document.domain = parentPageDomain;
+       }
+    }
+    window.onload = init;
+    </script>
+  </head>
+  <body>
+  <br />
+  </body>
+</html>


Property changes on: trunk/PhotoOp/root/static/js/src/widget/templates/richtextframe.html
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/validate.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/validate.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/validate.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,858 @@
+/*
+	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.validate");
+
+dojo.provide("dojo.widget.validate.Textbox");
+dojo.provide("dojo.widget.validate.ValidationTextbox");
+dojo.provide("dojo.widget.validate.IntegerTextbox");
+dojo.provide("dojo.widget.validate.RealNumberTextbox");
+dojo.provide("dojo.widget.validate.CurrencyTextbox");
+dojo.provide("dojo.widget.validate.IpAddressTextbox");
+dojo.provide("dojo.widget.validate.UrlTextbox");
+dojo.provide("dojo.widget.validate.EmailTextbox");
+dojo.provide("dojo.widget.validate.EmailListTextbox");
+dojo.provide("dojo.widget.validate.DateTextbox");
+dojo.provide("dojo.widget.validate.TimeTextbox");
+dojo.provide("dojo.widget.validate.UsStateTextbox");
+dojo.provide("dojo.widget.validate.UsZipTextbox");
+dojo.provide("dojo.widget.validate.UsPhoneNumberTextbox");
+
+dojo.require("dojo.widget.HtmlWidget");
+dojo.require("dojo.widget.Manager");
+dojo.require("dojo.widget.Parse");
+dojo.require("dojo.xml.Parse");
+dojo.require("dojo.lang");
+
+dojo.require("dojo.validate.common");
+dojo.require("dojo.validate.datetime");
+dojo.require("dojo.validate.check");
+dojo.require("dojo.validate.web");
+dojo.require("dojo.validate.us");
+
+dojo.widget.manager.registerWidgetPackage("dojo.widget.validate");
+
+
+/*
+  ****** Textbox ******
+
+  This widget is a generic textbox field.
+  Serves as a base class to derive more specialized functionality in subclasses.
+  Has the following properties that can be specified as attributes in the markup.
+
+  @attr id         The textbox id attribute.
+  @attr className  The textbox class attribute.
+  @attr name       The textbox name attribute.
+  @attr value      The textbox value attribute.
+  @attr trim       Removes leading and trailing whitespace if true.  Default is false.
+  @attr uppercase  Converts all characters to uppercase if true.  Default is false.
+  @attr lowercase  Converts all characters to lowercase if true.  Default is false.
+  @attr ucFirst    Converts the first character of each word to uppercase if true.
+  @attr lowercase  Removes all characters that are not digits if true.  Default is false.
+*/
+dojo.widget.validate.Textbox = function() {  }
+
+dojo.inherits(dojo.widget.validate.Textbox, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.validate.Textbox, {
+	// default values for new subclass properties
+	widgetId: "", 
+	widgetType: "Textbox", 
+	id: "",
+	className: "",
+	name: "",
+	value: "",
+	trim: false,
+	uppercase: false,
+	lowercase: false,
+	ucFirst: false,
+	digit: false,
+	htmlfloat: "none",
+	
+	templateString: "<span style='float:${this.htmlfloat};'><input dojoAttachPoint='textbox' dojoAttachEvent='onblur;onfocus'"
+					+ " id='${this.widgetId}' name='${this.name}' "
+					+ " value='${this.value}' class='${this.className}'></input></span>",
+
+	// our DOM nodes
+	textbox: null,
+
+	// Apply various filters to textbox value
+	filter: function() { 
+		if (this.trim) {
+			this.textbox.value = this.textbox.value.replace(/(^\s*|\s*$)/g, "");
+		} 
+		if (this.uppercase) {
+			this.textbox.value = this.textbox.value.toUpperCase();
+		} 
+		if (this.lowercase) {
+			this.textbox.value = this.textbox.value.toLowerCase();
+		} 
+		if (this.ucFirst) {
+			this.textbox.value = this.textbox.value.replace(/\b\w+\b/g, 
+				function(word) { return word.substring(0,1).toUpperCase() + word.substring(1).toLowerCase(); });
+		} 
+		if (this.digit) {
+			this.textbox.value = this.textbox.value.replace(/\D/g, "");
+		} 
+	},
+
+	// event handlers, you can over-ride these in your own subclasses
+	onfocus: function() {},
+	onblur: function() { this.filter(); },
+
+	// All functions below are called by create from dojo.widget.Widget
+	mixInProperties: function(localProperties, frag) {
+		dojo.widget.validate.Textbox.superclass.mixInProperties.apply(this, arguments);
+		if ( localProperties["class"] ) { 
+			this.className = localProperties["class"];
+		}
+	},
+
+	fillInTemplate: function() {
+		// apply any filters to initial value
+		this.filter();
+	}
+
+});
+
+dojo.widget.tags.addParseTreeHandler("dojo:Textbox");
+
+
+/*
+  ****** ValidationTextbox ******
+
+  A subclass of Textbox.
+  Over-ride isValid in subclasses to perform specific kinds of validation.
+  Has several new properties that can be specified as attributes in the markup.
+
+	@attr type          		Basic input tag type declaration.
+	@attr size          		Basic input tag size declaration.
+	@attr type          		Basic input tag maxlength declaration.	
+  @attr required          Can be true or false, default is false.
+  @attr validColor        The color textbox is highlighted for valid input. Default is #cfc.
+  @attr invalidColor      The color textbox is highlighted for invalid input. Default is #fcc.
+  @attr invalidClass			Class used to format displayed text in page if necessary to override default class
+  @attr invalidMessage    The message to display if value is invalid.
+  @attr missingMessage    The message to display if value is missing.
+  @attr missingClass		  Override default class used for missing input data
+  @attr listenOnKeyPress  Updates messages on each key press.  Default is true.
+  @attr promptMessage			Will not issue invalid message if field is populated with default user-prompt text
+*/
+dojo.widget.validate.ValidationTextbox = function() {}
+
+dojo.inherits(dojo.widget.validate.ValidationTextbox, dojo.widget.validate.Textbox);
+
+dojo.lang.extend(dojo.widget.validate.ValidationTextbox, {
+	// default values for new subclass properties
+	widgetType: "ValidationTextbox", 
+	type: "",
+	required: false,
+	validColor: "#cfc",
+	invalidColor: "#fcc",
+	rangeClass: "range",
+	invalidClass: "invalid",
+	missingClass: "missing",
+	size: "",
+	maxlength: "",
+	promptMessage: "",
+	invalidMessage: "* The value entered is not valid.",
+	missingMessage: "* This value is required.",
+	rangeMessage: "* This value out of range.",
+	listenOnKeyPress: true,
+	htmlfloat: "none",
+	lastCheckedValue: null,
+
+	templateString:   "<span style='float:${this.htmlfloat};'>"
+					+   "<input dojoAttachPoint='textbox' type='${this.type}' dojoAttachEvent='onblur;onfocus;onkeyup'"
+					+     " id='${this.widgetId}' name='${this.name}' size='${this.size}' maxlength='${this.maxlength}'"
+					+     " value='${this.value}' class='${this.className}' style=''></input>"
+					+   "<span dojoAttachPoint='invalidSpan' class='${this.invalidClass}'>${this.invalidMessage}</span>"
+					+   "<span dojoAttachPoint='missingSpan' class='${this.missingClass}'>${this.missingMessage}</span>"
+					+   "<span dojoAttachPoint='rangeSpan' class='${this.rangeClass}'>${this.rangeMessage}</span>"
+					+ "</span>",
+
+	// new DOM nodes
+	invalidSpan: null,
+	missingSpan: null,
+	rangeSpan: null,
+
+	getValue: function() {
+		return this.textbox.value;
+	},
+
+	setValue: function(value) {
+		this.textbox.value = value;
+		this.update();
+	},
+
+	// Need to over-ride with your own validation code in subclasses
+	isValid: function() { return true; },
+
+	// Need to over-ride with your own validation code in subclasses
+	isInRange: function() { return true; },
+
+	// Returns true if value is all whitespace
+	isEmpty: function() { 
+		return ( /^\s*$/.test(this.textbox.value) );
+	},
+
+	// Returns true if value is required and it is all whitespace.
+	isMissing: function() { 
+		return ( this.required && this.isEmpty() );
+	},
+
+	// Called oninit, onblur, and onkeypress.
+	// Show missing or invalid messages if appropriate, and highlight textbox field.
+	update: function() {
+		this.lastCheckedValue = this.textbox.value;
+		this.missingSpan.style.display = "none";
+		this.invalidSpan.style.display = "none";
+		this.rangeSpan.style.display = "none";
+
+		var empty = this.isEmpty();
+		var valid = true;
+		if(this.promptMessage != this.textbox.value){ 
+			valid = this.isValid(); 
+		}
+		var missing = this.isMissing();
+
+		// Display at most one error message
+		if(missing){
+			this.missingSpan.style.display = "";
+		}else if( !empty && !valid ){
+			this.invalidSpan.style.display = "";
+		}else if( !empty && !this.isInRange() ){
+			this.rangeSpan.style.display = "";
+		}
+		this.highlight();
+	},
+
+	// Called oninit, and onblur.
+	highlight: function() {
+		// highlight textbox background 
+		if ( this.isEmpty() ) {
+			this.textbox.style.backgroundColor = "";
+		}else if ( this.isValid() && this.isInRange() ){
+			this.textbox.style.backgroundColor = this.validColor;
+		}else if( this.textbox.value != this.promptMessage){ 
+			this.textbox.style.backgroundColor = this.invalidColor;
+		}
+	},
+
+	onfocus: function() {
+		if ( !this.listenOnKeyPress) {
+		    this.textbox.style.backgroundColor = "";
+		}
+	},
+
+	onblur: function() { 
+		this.filter();
+		this.update(); 
+	},
+
+	onkeyup: function(){ 
+		if(this.listenOnKeyPress){ 
+			//this.filter();  trim is problem if you have to type two words
+			this.update(); 
+		}else if (this.textbox.value != this.lastCheckedValue){
+		    this.textbox.style.backgroundColor = "";
+		}
+	},
+
+	// FIXME: why are there to fillInTemplate methods defined here?
+	fillInTemplate: function() {
+		// Attach isMissing and isValid methods to the textbox.
+		// We may use them later in connection with a submit button widget.
+		// TODO: this is unorthodox; it seems better to do it another way -- Bill
+		this.textbox.isValid = function() { this.isValid.call(this); };
+		this.textbox.isMissing = function() { this.isMissing.call(this); };
+		this.textbox.isInRange = function() { this.isInRange.call(this); };
+		this.filter();
+		this.update(); 
+	}
+});
+
+dojo.widget.tags.addParseTreeHandler("dojo:ValidationTextbox");
+
+
+/*
+  ****** IntegerTextbox ******
+
+  A subclass of ValidationTextbox.
+  Over-rides isValid/isInRange to test for integer input.
+  Has 4 new properties that can be specified as attributes in the markup.
+
+  @attr signed     The leading plus-or-minus sign. Can be true or false, default is either.
+  @attr separator  The character used as the thousands separator.  Default is no separator.
+  @attr min  Minimum signed value.  Default is -Infinity
+  @attr max  Maximum signed value.  Default is +Infinity
+*/
+dojo.widget.validate.IntegerTextbox = function(node) {
+	// this property isn't a primitive and needs to be created on a per-item basis.
+	this.flags = {};
+}
+
+dojo.inherits(dojo.widget.validate.IntegerTextbox, dojo.widget.validate.ValidationTextbox);
+
+dojo.lang.extend(dojo.widget.validate.IntegerTextbox, {
+	// new subclass properties
+	widgetType: "IntegerTextbox", 
+
+	mixInProperties: function(localProperties, frag) {
+		// First initialize properties in super-class.
+		dojo.widget.validate.IntegerTextbox.superclass.mixInProperties.apply(this, arguments);
+
+		// Get properties from markup attibutes, and assign to flags object.
+		if((localProperties.signed == "true")||
+			(localProperties.signed == "always")){
+			this.flags.signed = true;
+		}else if((localProperties.signed == "false")||
+				(localProperties.signed == "never")){
+			this.flags.signed = false;
+			this.flags.min = 0;
+		}else{
+			this.flags.signed = [ true, false ]; // optional
+		}
+		if(localProperties.separator){ 
+			this.flags.separator = localProperties.separator;
+		}
+		if(localProperties.min){ 
+			this.flags.min = parseInt(localProperties.min);
+		}
+		if(localProperties.max){ 
+			this.flags.max = parseInt(localProperties.max);
+		}
+	},
+
+	// Over-ride for integer validation
+	isValid: function() { 
+		return dojo.validate.isInteger(this.textbox.value, this.flags);
+	},
+	isInRange: function() { 
+		return dojo.validate.isInRange(this.textbox.value, this.flags);
+	}
+
+});
+
+dojo.widget.tags.addParseTreeHandler("dojo:IntegerTextbox");
+
+
+/*
+  ****** RealNumberTextbox ******
+
+  A subclass that extends IntegerTextbox.
+  Over-rides isValid/isInRange to test for real number input.
+  Has 5 new properties that can be specified as attributes in the markup.
+
+  @attr places    The exact number of decimal places.  If omitted, it's unlimited and optional.
+  @attr exponent  Can be true or false.  If omitted the exponential part is optional.
+  @attr eSigned   Is the exponent signed?  Can be true or false, if omitted the sign is optional.
+  @attr min  Minimum signed value.  Default is -Infinity
+  @attr max  Maximum signed value.  Default is +Infinity
+*/
+dojo.widget.validate.RealNumberTextbox = function(node) {
+	this.flags = {};
+}
+
+dojo.inherits(dojo.widget.validate.RealNumberTextbox, dojo.widget.validate.IntegerTextbox);
+
+dojo.lang.extend(dojo.widget.validate.RealNumberTextbox, {
+	// new subclass properties
+	widgetType: "RealNumberTextbox", 
+
+	mixInProperties: function(localProperties, frag) {
+		// First initialize properties in super-class.
+		dojo.widget.validate.RealNumberTextbox.superclass.mixInProperties.apply(this, arguments);
+
+		// Get properties from markup attibutes, and assign to flags object.
+		if ( localProperties.places ) { 
+			this.flags.places = Number( localProperties.places );
+		}
+		if((localProperties.exponent == "true")||
+			(localProperties.exponent == "always")){
+			this.flags.exponent = true;
+		}else if((localProperties.exponent == "false")||(localProperties.exponent == "never")){
+			this.flags.exponent = false;
+		}else{
+			this.flags.exponent = [ true, false ]; // optional
+		}
+		if((localProperties.esigned == "true")||(localProperties.esigned == "always")){
+			this.flags.eSigned = true;
+		}else if((localProperties.esigned == "false")||(localProperties.esigned == "never")){
+			this.flags.eSigned = false;
+		}else{
+			this.flags.eSigned = [ true, false ]; // optional
+		}
+		if(localProperties.min){ 
+			this.flags.min = parseFloat(localProperties.min);
+		}
+		if(localProperties.max){ 
+			this.flags.max = parseFloat(localProperties.max);
+		}
+	},
+
+	// Over-ride for real number validation
+	isValid: function() { 
+		return dojo.validate.isRealNumber(this.textbox.value, this.flags);
+	},
+	isInRange: function() { 
+		return dojo.validate.isInRange(this.textbox.value, this.flags);
+	}
+
+});
+
+dojo.widget.tags.addParseTreeHandler("dojo:RealNumberTextbox");
+
+
+/*
+  ****** CurrencyTextbox ******
+
+  A subclass that extends IntegerTextbox.
+  Over-rides isValid/isInRange to test if input denotes a monetary value .
+  Has 5 new properties that can be specified as attributes in the markup.
+
+  @attr cents      The two decimal places for cents.  Can be true or false, optional if omitted.
+  @attr symbol     A currency symbol such as Yen "???", Pound "???", or the Euro "???". Default is "$".
+  @attr separator  Default is "," instead of no separator as in IntegerTextbox.
+  @attr min  Minimum signed value.  Default is -Infinity
+  @attr max  Maximum signed value.  Default is +Infinity
+*/
+dojo.widget.validate.CurrencyTextbox = function(node) {
+	this.flags = {};
+}
+
+dojo.inherits(dojo.widget.validate.CurrencyTextbox, dojo.widget.validate.IntegerTextbox);
+
+dojo.lang.extend(dojo.widget.validate.CurrencyTextbox, {
+	// new subclass properties
+	widgetType: "CurrencyTextbox", 
+
+	mixInProperties: function(localProperties, frag) {
+		// First initialize properties in super-class.
+		dojo.widget.validate.CurrencyTextbox.superclass.mixInProperties.apply(this, arguments);
+
+		// Get properties from markup attibutes, and assign to flags object.
+		if ( localProperties.cents ) { 
+			this.flags.cents = ( localProperties.cents == "true" );
+		}
+		if ( localProperties.symbol ) { 
+			this.flags.symbol = localProperties.symbol;
+		}
+		if(localProperties.min){ 
+			this.flags.min = parseFloat(localProperties.min);
+		}
+		if(localProperties.max){ 
+			this.flags.max = parseFloat(localProperties.max);
+		}
+	},
+
+	// Over-ride for currency validation
+	isValid: function() { 
+		return dojo.validate.isCurrency(this.textbox.value, this.flags);
+	},
+	isInRange: function() { 
+		return dojo.validate.isInRange(this.textbox.value, this.flags);
+	}
+
+});
+
+dojo.widget.tags.addParseTreeHandler("dojo:CurrencyTextbox");
+
+
+/*
+  ****** IpAddressTextbox ******
+
+  A subclass of ValidationTextbox.
+  Over-rides isValid to test for IP addresses.
+  Can specify formats for ipv4 or ipv6 as attributes in the markup.
+
+  @attr allowDottedDecimal  true or false, default is true.
+  @attr allowDottedHex      true or false, default is true.
+  @attr allowDottedOctal    true or false, default is true.
+  @attr allowDecimal        true or false, default is true.
+  @attr allowHex            true or false, default is true.
+  @attr allowIPv6           true or false, default is true.
+  @attr allowHybrid         true or false, default is true.
+*/
+dojo.widget.validate.IpAddressTextbox = function(node) {
+	this.flags = {};
+}
+
+dojo.inherits(dojo.widget.validate.IpAddressTextbox, dojo.widget.validate.ValidationTextbox);
+
+dojo.lang.extend(dojo.widget.validate.IpAddressTextbox, {
+	// new subclass properties
+	widgetType: "IpAddressTextbox", 
+
+	mixInProperties: function(localProperties, frag) {
+		// First initialize properties in super-class.
+		dojo.widget.validate.IpAddressTextbox.superclass.mixInProperties.apply(this, arguments);
+
+		// Get properties from markup attibutes, and assign to flags object.
+		if ( localProperties.allowdotteddecimal ) { 
+			this.flags.allowDottedDecimal = ( localProperties.allowdotteddecimal == "true" );
+		}
+		if ( localProperties.allowdottedhex ) { 
+			this.flags.allowDottedHex = ( localProperties.allowdottedhex == "true" );
+		}
+		if ( localProperties.allowdottedoctal ) { 
+			this.flags.allowDottedOctal = ( localProperties.allowdottedoctal == "true" );
+		}
+		if ( localProperties.allowdecimal ) { 
+			this.flags.allowDecimal = ( localProperties.allowdecimal == "true" );
+		}
+		if ( localProperties.allowhex ) { 
+			this.flags.allowHex = ( localProperties.allowhex == "true" );
+		}
+		if ( localProperties.allowipv6 ) { 
+			this.flags.allowIPv6 = ( localProperties.allowipv6 == "true" );
+		}
+		if ( localProperties.allowhybrid ) { 
+			this.flags.allowHybrid = ( localProperties.allowhybrid == "true" );
+		}
+	},
+
+	// Over-ride for IP address validation
+	isValid: function() { 
+		return dojo.validate.isIpAddress(this.textbox.value, this.flags);
+	}
+});
+
+dojo.widget.tags.addParseTreeHandler("dojo:IpAddressTextbox");
+
+
+/*
+  ****** UrlTextbox ******
+
+  A subclass of IpAddressTextbox.
+  Over-rides isValid to test for URL's.
+  Can specify 5 additional attributes in the markup.
+
+  @attr scheme        Can be true or false.  If omitted the scheme is optional.
+  @attr allowIP       Allow an IP address for hostname.  Default is true.
+  @attr allowLocal    Allow the host to be "localhost".  Default is false.
+  @attr allowCC       Allow 2 letter country code domains.  Default is true.
+  @attr allowGeneric  Allow generic domains.  Can be true or false, default is true.
+*/
+dojo.widget.validate.UrlTextbox = function(node) {
+	this.flags = {};
+}
+
+dojo.inherits(dojo.widget.validate.UrlTextbox, dojo.widget.validate.IpAddressTextbox);
+
+dojo.lang.extend(dojo.widget.validate.UrlTextbox, {
+	// new subclass properties
+	widgetType: "UrlTextbox", 
+
+	mixInProperties: function(localProperties, frag) {
+		// First initialize properties in super-class.
+		dojo.widget.validate.UrlTextbox.superclass.mixInProperties.apply(this, arguments);
+
+		// Get properties from markup attibutes, and assign to flags object.
+		if ( localProperties.scheme ) { 
+			this.flags.scheme = ( localProperties.scheme == "true" );
+		}
+		if ( localProperties.allowip ) { 
+			this.flags.allowIP = ( localProperties.allowip == "true" );
+		}
+		if ( localProperties.allowlocal ) { 
+			this.flags.allowLocal = ( localProperties.allowlocal == "true" );
+		}
+		if ( localProperties.allowcc ) { 
+			this.flags.allowCC = ( localProperties.allowcc == "true" );
+		}
+		if ( localProperties.allowgeneric ) { 
+			this.flags.allowGeneric = ( localProperties.allowgeneric == "true" );
+		}
+	},
+
+	// Over-ride for URL validation
+	isValid: function() { 
+		return dojo.validate.isUrl(this.textbox.value, this.flags);
+	}
+
+});
+
+dojo.widget.tags.addParseTreeHandler("dojo:UrlTextbox");
+
+
+/*
+  ****** EmailTextbox ******
+
+  A subclass of UrlTextbox.
+  Over-rides isValid to test for email addresses.
+  Can use all markup attributes/properties of UrlTextbox except scheme.
+  One new attribute available in the markup.
+
+  @attr allowCruft  Allow address like <mailto:foo at yahoo.com>.  Default is false.
+*/
+dojo.widget.validate.EmailTextbox = function(node) {
+	this.flags = {};
+}
+
+dojo.inherits(dojo.widget.validate.EmailTextbox, dojo.widget.validate.UrlTextbox);
+
+dojo.lang.extend(dojo.widget.validate.EmailTextbox, {
+	// new subclass properties
+	widgetType: "EmailTextbox", 
+
+	mixInProperties: function(localProperties, frag) {
+		// First initialize properties in super-class.
+		dojo.widget.validate.EmailTextbox.superclass.mixInProperties.apply(this, arguments);
+
+		// Get properties from markup attibutes, and assign to flags object.
+		if ( localProperties.allowcruft ) { 
+			this.flags.allowCruft = ( localProperties.allowcruft == "true" );
+		}
+	},
+
+	// Over-ride for email address validation
+	isValid: function() { 
+		return dojo.validate.isEmailAddress(this.textbox.value, this.flags);
+	}
+
+});
+
+dojo.widget.tags.addParseTreeHandler("dojo:EmailTextbox");
+
+
+/*
+  ****** EmailListTextbox ******
+
+  A subclass of EmailTextbox.
+  Over-rides isValid to test for a list of email addresses.
+  Can use all markup attributes/properties of EmailTextbox and ...
+
+  @attr listSeparator  The character used to separate email addresses.  
+    Default is ";", ",", "\n" or " ".
+*/
+dojo.widget.validate.EmailListTextbox = function(node) {
+	this.flags = {};
+}
+
+dojo.inherits(dojo.widget.validate.EmailListTextbox, dojo.widget.validate.EmailTextbox);
+
+dojo.lang.extend(dojo.widget.validate.EmailListTextbox, {
+	// new subclass properties
+	widgetType: "EmailListTextbox", 
+
+	mixInProperties: function(localProperties, frag) {
+		// First initialize properties in super-class.
+		dojo.widget.validate.EmailListTextbox.superclass.mixInProperties.apply(this, arguments);
+
+		// Get properties from markup attibutes, and assign to flags object.
+		if ( localProperties.listseparator ) { 
+			this.flags.listSeparator = localProperties.listseparator;
+		}
+	},
+
+	// Over-ride for email address list validation
+	isValid: function() { 
+		return dojo.validate.isEmailAddressList(this.textbox.value, this.flags);
+	}
+
+});
+
+dojo.widget.tags.addParseTreeHandler("dojo:EmailListTextbox");
+
+
+/*
+  ****** DateTextbox ******
+
+  A subclass of ValidationTextbox.
+  Over-rides isValid to test if input is in a valid date format.
+
+  @attr format  Described in dojo.validate.js.  Default is  "MM/DD/YYYY".
+*/
+dojo.widget.validate.DateTextbox = function(node) {
+	this.flags = {};
+}
+
+dojo.inherits(dojo.widget.validate.DateTextbox, dojo.widget.validate.ValidationTextbox);
+
+dojo.lang.extend(dojo.widget.validate.DateTextbox, {
+	// new subclass properties
+	widgetType: "DateTextbox", 
+
+	mixInProperties: function(localProperties, frag) {
+		// First initialize properties in super-class.
+		dojo.widget.validate.DateTextbox.superclass.mixInProperties.apply(this, arguments);
+
+		// Get properties from markup attibutes, and assign to flags object.
+		if ( localProperties.format ) { 
+			this.flags.format = localProperties.format;
+		}
+	},
+
+	// Over-ride for date validation
+	isValid: function() { 
+		return dojo.validate.isValidDate(this.textbox.value, this.flags.format);
+	}
+
+});
+
+dojo.widget.tags.addParseTreeHandler("dojo:DateTextbox");
+
+
+/*
+  ****** TimeTextbox ******
+
+  A subclass of ValidationTextbox.
+  Over-rides isValid to test if input is in a valid time format.
+
+  @attr format    Described in dojo.validate.js.  Default is  "h:mm:ss t".
+  @attr amSymbol  The symbol used for AM.  Default is "AM" or "am".
+  @attr pmSymbol  The symbol used for PM.  Default is "PM" or "pm".
+*/
+dojo.widget.validate.TimeTextbox = function(node) {
+	this.flags = {};
+}
+
+dojo.inherits(dojo.widget.validate.TimeTextbox, dojo.widget.validate.ValidationTextbox);
+
+dojo.lang.extend(dojo.widget.validate.TimeTextbox, {
+	// new subclass properties
+	widgetType: "TimeTextbox", 
+
+	mixInProperties: function(localProperties, frag) {
+		// First initialize properties in super-class.
+		dojo.widget.validate.TimeTextbox.superclass.mixInProperties.apply(this, arguments);
+
+		// Get properties from markup attibutes, and assign to flags object.
+		if ( localProperties.format ) { 
+			this.flags.format = localProperties.format;
+		}
+		if ( localProperties.amsymbol ) { 
+			this.flags.amSymbol = localProperties.amsymbol;
+		}
+		if ( localProperties.pmsymbol ) { 
+			this.flags.pmSymbol = localProperties.pmsymbol;
+		}
+	},
+
+	// Over-ride for time validation
+	isValid: function() { 
+		return dojo.validate.isValidTime(this.textbox.value, this.flags);
+	}
+
+});
+
+dojo.widget.tags.addParseTreeHandler("dojo:TimeTextbox");
+
+
+/*
+  ****** UsStateTextbox ******
+
+  A subclass of ValidationTextbox.
+  Over-rides isValid to test if input is a US state abbr.
+
+  @attr allowTerritories  Allow Guam, Puerto Rico, etc.  Default is true.
+  @attr allowMilitary     Allow military 'states', e.g. Armed Forces Europe (AE). Default is true.
+*/
+dojo.widget.validate.UsStateTextbox = function(node) {
+	this.flags = {};
+}
+
+dojo.inherits(dojo.widget.validate.UsStateTextbox, dojo.widget.validate.ValidationTextbox);
+
+dojo.lang.extend(dojo.widget.validate.UsStateTextbox, {
+	// new subclass properties
+	widgetType: "UsStateTextbox", 
+
+	mixInProperties: function(localProperties, frag) {
+		// Initialize properties in super-class.
+		dojo.widget.validate.UsStateTextbox.superclass.mixInProperties.apply(this, arguments);
+
+		// Get properties from markup attibutes, and assign to flags object.
+		if ( localProperties.allowterritories ) { 
+			this.flags.allowTerritories = ( localProperties.allowterritories == "true" );
+		}
+		if ( localProperties.allowmilitary ) { 
+			this.flags.allowMilitary = ( localProperties.allowmilitary == "true" );
+		}
+	},
+
+	isValid: function() { 
+		return dojo.validate.us.isState(this.textbox.value, this.flags);
+	}
+
+});
+
+dojo.widget.tags.addParseTreeHandler("dojo:UsStateTextbox");
+
+
+/*
+  ****** UsZipTextbox ******
+
+  A subclass of ValidationTextbox.
+  Over-rides isValid to test if input is a US zip code.
+  Validates zip-5 and zip-5 plus 4.
+*/
+dojo.widget.validate.UsZipTextbox = function(node) {}
+
+dojo.inherits(dojo.widget.validate.UsZipTextbox, dojo.widget.validate.ValidationTextbox);
+
+dojo.lang.extend(dojo.widget.validate.UsZipTextbox, {
+	// new subclass properties
+	widgetType: "UsZipTextbox", 
+
+	isValid: function() { 
+		return dojo.validate.us.isZipCode(this.textbox.value);
+	}
+
+});
+
+dojo.widget.tags.addParseTreeHandler("dojo:UsZipTextbox");
+
+
+/*
+  ****** UsSocialSecurityNumberTextbox ******
+
+  A subclass of ValidationTextbox.
+  Over-rides isValid to test if input is a US Social Security Number.
+*/
+dojo.widget.validate.UsSocialSecurityNumberTextbox = function(node) {}
+
+dojo.inherits(dojo.widget.validate.UsSocialSecurityNumberTextbox, dojo.widget.validate.ValidationTextbox);
+
+dojo.lang.extend(dojo.widget.validate.UsSocialSecurityNumberTextbox, {
+	// new subclass properties
+	widgetType: "UsSocialSecurityNumberTextbox", 
+
+	isValid: function() { 
+		return dojo.validate.us.isSocialSecurityNumber(this.textbox.value);
+	}
+
+});
+
+dojo.widget.tags.addParseTreeHandler("dojo:UsSocialSecurityNumberTextbox");
+
+
+/*
+  ****** UsPhoneNumberTextbox ******
+
+  A subclass of ValidationTextbox.
+  Over-rides isValid to test if input is a 10-digit US phone number, an extension is optional.
+*/
+dojo.widget.validate.UsPhoneNumberTextbox = function(node) {}
+
+dojo.inherits(dojo.widget.validate.UsPhoneNumberTextbox, dojo.widget.validate.ValidationTextbox);
+
+dojo.lang.extend(dojo.widget.validate.UsPhoneNumberTextbox, {
+	// new subclass properties
+	widgetType: "UsPhoneNumberTextbox", 
+
+	isValid: function() { 
+		return dojo.validate.us.isPhoneNumber(this.textbox.value);
+	}
+
+});
+
+dojo.widget.tags.addParseTreeHandler("dojo:UsPhoneNumberTextbox");


Property changes on: trunk/PhotoOp/root/static/js/src/widget/validate.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/widget/vml/Chart.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/widget/vml/Chart.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/widget/vml/Chart.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,426 @@
+/*
+	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.vml.Chart");
+
+dojo.require("dojo.widget.HtmlWidget");
+dojo.require("dojo.widget.Chart");
+dojo.require("dojo.math");
+dojo.require("dojo.html");
+//dojo.require("dojo.vml");
+dojo.require("dojo.graphics.color");
+
+dojo.widget.vml.Chart=function(){
+	dojo.widget.Chart.call(this);
+	dojo.widget.HtmlWidget.call(this);
+};
+dojo.inherits(dojo.widget.vml.Chart, dojo.widget.HtmlWidget);
+dojo.lang.extend(dojo.widget.vml.Chart, {
+	//	widget props
+	templatePath:null,
+	templateCssPath:null,
+
+	//	state
+	_isInitialized:false,
+	hasData:false,
+
+	//	chart props
+	vectorNode:null,
+	plotArea:null,
+	dataGroup:null,
+	axisGroup:null,
+
+	properties:{
+		height:400,	//	defaults, will resize to the domNode.
+		width:600,
+		plotType:null,
+		padding:{
+			top:10,
+			bottom:2,
+			left:60,
+			right:30
+		},
+		axes:{
+			x:{
+				plotAt:0,
+				label:"",
+				unitLabel:"",
+				unitType:Number,
+				nUnitsToShow:10,
+				range:{
+					min:0,
+					max:200
+				}
+			},
+			y:{
+				plotAt:0,
+				label:"",
+				unitLabel:"",
+				unitType:Number,
+				nUnitsToShow:10,
+				range:{
+					min:0,
+					max:200
+				}
+			}
+		}
+	},
+	
+	fillInTemplate:function(args,frag){
+		this.initialize();
+		this.render();
+	},
+	parseData:function(){
+	},
+	initialize:function(){
+		//	parse the data first.
+		this.parseData();
+	
+		// render the body of the chart, not the chart data.
+		if(this.vectorNode){ this.destroy(); }
+		this.vectorNode=document.createElement("div");
+		this.vectorNode.style.width=this.properties.width+"px";
+		this.vectorNode.style.height=this.properties.height+"px";
+		this.vectorNode.style.position="relative";
+		this.domNode.appendChild(this.vectorNode);
+
+		var plotWidth=this.properties.width-this.properties.padding.left-this.properties.padding.right;
+		var plotHeight=this.properties.height-this.properties.padding.top-this.properties.padding.bottom;
+
+		this.plotArea=document.createElement("div");
+		this.plotArea.style.position="absolute";
+		this.plotArea.style.backgroundColor="#fff";
+		this.plotArea.style.top=(this.properties.padding.top)-2+"px";
+		this.plotArea.style.left=(this.properties.padding.left-1)+"px";
+		this.plotArea.style.width=plotWidth+"px";
+		this.plotArea.style.height=plotHeight+"px";
+		this.vectorNode.appendChild(this.plotArea);
+		
+		this.dataGroup=document.createElement("div");
+		this.dataGroup.style.position="relative";
+		this.plotArea.appendChild(this.dataGroup);
+
+		//	clipping rects, what a fucking pain.
+		var bg=this.domNode.style.backgroundColor;
+		var r=document.createElement("v:rect");
+		r.setAttribute("fillcolor", bg);
+		r.setAttribute("stroked", "false");
+		r.style.position="absolute";
+		r.style.top=(-1*this.properties.padding.top)-1+"px";
+		r.style.left=(-1*this.properties.padding.left)+"px";
+		r.style.width=(this.properties.width-3)+"px";
+		r.style.height=(this.properties.padding.top)-2+"px";
+		this.vectorNode.appendChild(r);
+
+		r=document.createElement("v:rect");
+		r.setAttribute("fillcolor", bg);
+		r.setAttribute("stroked", "false");
+		r.style.position="absolute";
+		r.style.top=plotHeight-2+"px";
+		r.style.left=(-1*this.properties.padding.left)+"px";
+		r.style.width=(this.properties.width-3)+"px";
+		r.style.height=(this.properties.padding.bottom)-2+"px"; // fixme: check this.
+		this.vectorNode.appendChild(r);
+
+		r=document.createElement("v:rect");
+		r.setAttribute("fillcolor", bg);
+		r.setAttribute("stroked", "false");
+		r.style.position="absolute";
+		r.style.top="-2px";
+		r.style.left=(-1*this.properties.padding.left)+"px";
+		r.style.width=(this.properties.padding.left-1)+"px";
+		r.style.height=plotHeight+"px";
+		this.vectorNode.appendChild(r);
+		
+		r=document.createElement("v:rect");
+		r.setAttribute("fillcolor", bg);
+		r.setAttribute("stroked", "false");
+		r.style.position="absolute";
+		r.style.top="-2px";
+		r.style.right=(-1*this.properties.padding.right)+1+"px";
+		r.style.width=(this.properties.padding.right-1)+"px";
+		r.style.height=plotHeight+"px";
+		this.vectorNode.appendChild(r);
+		//	end clipping rects.  god that sucks, i wish VML had clipping outside of that crap vmlframe...
+
+		this.axisGroup=document.createElement("div");
+		this.axisGroup.style.position="relative";
+		this.plotArea.appendChild(this.axisGroup);
+
+		var stroke=1;
+
+		//	x axis
+		var line=document.createElement("v:line");
+		var y=dojo.widget.vml.Chart.Plotter.getY(this.properties.axes.x.plotAt, this);
+		line.setAttribute("from", this.properties.padding.left-stroke + "," + y);
+		line.setAttribute("to", plotWidth + "," + y);
+		line.style.position="absolute";
+		line.style.antialias="false";
+		line.setAttribute("strokecolor", "#666");
+		line.setAttribute("strokeweight", stroke*2+"px");
+		this.axisGroup.appendChild(line);
+
+		//	y axis
+		var line=document.createElement("v:line");
+		var y=dojo.widget.vml.Chart.Plotter.getX(this.properties.axes.y.plotAt, this);
+		line.setAttribute("from", y+","+this.properties.padding.top);
+		line.setAttribute("to", y+","+this.properties.height-this.properties.padding.bottom);
+		line.style.position="absolute";
+		line.style.antialias="false";
+		line.setAttribute("strokecolor", "#666");
+		line.setAttribute("strokeweight", stroke*2+"px");
+		this.axisGroup.appendChild(line);
+		
+		//	labels
+		var size=10;
+
+		//	x axis labels.
+		var t=document.createElement("div");
+		t.style.position="absolute";
+		t.style.top=(this.properties.height-this.properties.padding.bottom+size+2)+"px";
+		t.style.left=this.properties.padding.left+"px";
+		t.style.fontFamily="sans-serif";
+		t.style.fontSize=size+"px";
+		t.innerHTML=dojo.math.round(parseFloat(this.properties.axes.x.range.min),2);
+		this.axisGroup.appendChild(t);
+
+		t=document.createElement("div");
+		t.style.position="absolute";
+		t.style.top=(this.properties.height-this.properties.padding.bottom+size+2)+"px";
+		t.style.left=(this.properties.width-this.properties.padding.right-(size/2))+"px";
+		t.style.fontFamily="sans-serif";
+		t.style.fontSize=size+"px";
+		t.innerHTML=dojo.math.round(parseFloat(this.properties.axes.x.range.max),2);
+		this.axisGroup.appendChild(t);
+
+		//	y axis labels.
+		t=document.createElement("div");
+		t.style.position="absolute";
+		t.style.top=-1*(size/2)+"px";
+		t.style.right=(plotWidth+4)+"px";
+		t.style.fontFamily="sans-serif";
+		t.style.fontSize=size+"px";
+		t.innerHTML=dojo.math.round(parseFloat(this.properties.axes.y.range.max),2);
+		this.axisGroup.appendChild(t);
+		
+		t=document.createElement("div");
+		t.style.position="absolute";
+		t.style.top=(this.properties.height-this.properties.padding.bottom)+"px";
+		t.style.right=(plotWidth+4)+"px";
+		t.style.fontFamily="sans-serif";
+		t.style.fontSize=size+"px";
+		t.innerHTML=dojo.math.round(parseFloat(this.properties.axes.y.range.min),2);
+		this.axisGroup.appendChild(t);
+		
+		//	this is last.
+		this.assignColors();
+		this._isInitialized=true;
+	},
+	destroy:function(){
+		while(this.domNode.childNodes.length>0){
+			this.domNode.removeChild(this.domNode.childNodes[0]);
+		}
+		this.vectorNode=this.plotArea=this.dataGroup=this.axisGroup=null;
+	},
+	render:function(){
+		if (this.dataGroup){
+			while(this.dataGroup.childNodes.length>0){
+				this.dataGroup.removeChild(this.dataGroup.childNodes[0]);
+			}
+		} else {
+			this.initialize();
+		}
+		for(var i=0; i<this.series.length; i++){
+			dojo.widget.vml.Chart.Plotter.plot(this.series[i], this);
+		}
+	}
+});
+
+dojo.widget.vml.Chart.Plotter=new function(){
+	var _this=this;
+	var plotters = {};
+	var types=dojo.widget.Chart.PlotTypes;
+	
+	this.getX=function(value, chart){
+		var v=parseFloat(value);
+		var min=chart.properties.axes.x.range.min;
+		var max=chart.properties.axes.x.range.max;
+		var ofst=0-min;
+		min+=ofst; max+=ofst; v+=ofst;
+
+		var xmin=chart.properties.padding.left;
+		var xmax=chart.properties.width-chart.properties.padding.right;
+		var x=(v*((xmax-xmin)/max))+xmin;
+		return x;
+	};
+	this.getY=function(value, chart){
+		var v=parseFloat(value);
+		var max=chart.properties.axes.y.range.max;
+		var min=chart.properties.axes.y.range.min;
+		var ofst=0;
+		if(min<0)ofst+=Math.abs(min);
+		min+=ofst; max+=ofst; v+=ofst;
+		
+		var ymin=chart.properties.height-chart.properties.padding.bottom;
+		var ymax=chart.properties.padding.top;
+		var y=(((ymin-ymax)/(max-min))*(max-v))+ymax;
+		return y;
+	};
+
+	this.addPlotter=function(name, func){
+		plotters[name]=func;
+	};
+	this.plot=function(series, chart){
+		if (series.values.length==0) return;
+		if (series.plotType && plotters[series.plotType]){
+			return plotters[series.plotType](series, chart);
+		}
+		else if (chart.plotType && plotters[chart.plotType]){
+			return plotters[chart.plotType](series, chart);
+		}
+	};
+
+	//	plotting
+	plotters[types.Bar]=function(series, chart){
+		var space=1;
+		var lastW = 0;
+		for (var i=0; i<series.values.length; i++){
+			var x=_this.getX(series.values[i].x, chart);
+			var w;
+			if (i==series.values.length-1){
+				w=lastW;
+			} else{
+				w=_this.getX(series.values[i+1].x, chart)-x-space;
+				lastW=w;
+			}
+			x-=(w/2);
+
+			var yA=_this.getY(chart.properties.axes.x.plotAt, chart);
+			var y=_this.getY(series.values[i].value, chart);
+			var h=Math.abs(yA-y);
+			if (parseFloat(series.values[i].value)<chart.properties.axes.x.plotAt){
+				var oy=yA;
+				yA=y;
+				y=oy;
+			}
+
+			var bar=document.createElement("v:rect");
+			bar.style.position="absolute";
+			bar.style.top=x+"px";
+			bar.style.left=y+"px";
+			bar.style.width=w+"px";
+			bar.style.height=h+"px";
+			bar.setAttribute("fillColor", series.color);
+			bar.setAttribute("title", series.label + ": " + series.values[i].value);
+			bar.setAttribute("coordsize", chart.properties.width + "," + chart.properties.height);
+			var fill=document.createElement("v:fill");
+			fill.setAttribute("opacity", "0.9");
+			bar.appendChild(fill);
+			chart.dataGroup.appendChild(bar);
+		}
+	};	
+	plotters[types.Line]=function(series, chart){
+		var tension=3;
+
+		var line=document.createElement("v:shape");
+		line.setAttribute("strokeweight", "2px");
+		line.setAttribute("strokecolor", series.color);
+		line.setAttribute("fillcolor", "none");
+		line.setAttribute("filled", "false");
+		line.setAttribute("title", series.label);
+		line.setAttribute("coordsize", chart.properties.width + "," + chart.properties.height);
+		line.style.position="absolute";
+		line.style.top="0px";
+		line.style.left="0px";
+		line.style.width= chart.properties.width+"px";
+		line.style.height=chart.properties.height+"px";
+		var stroke=document.createElement("v:stroke");
+		stroke.setAttribute("opacity", "0.85");
+		line.appendChild(stroke);
+
+		var path = [];
+		for (var i=0; i<series.values.length; i++){
+			var x = _this.getX(series.values[i].x, chart)
+			var y = _this.getY(series.values[i].value, chart);
+
+			if (i==0){
+				path.push("m");
+				path.push(x+","+y);
+			}else{
+				var lastx=_this.getX(series.values[i-1].x, chart);
+				var lasty=_this.getY(series.values[i-1].value, chart);
+				var dx=x-lastx;
+				
+				path.push("v");
+				var cx=x-(tension-1)*(dx/tension);
+				path.push(cx+",0");
+				cx=x-(dx/tension);
+				path.push(cx+","+y-lasty);
+				path.push(dx, y-lasty);
+			}
+		}
+		line.setAttribute("path", path.join(" ")+" e");
+		chart.dataGroup.appendChild(line);
+	};
+	plotters[types.Scatter]=function(series, chart){
+		var r=8;
+		for (var i=0; i<series.values.length; i++){
+			var x=_this.getX(series.values[i].x, chart);
+			var y=_this.getY(series.values[i].value, chart);
+			var mod=r/2;
+
+			var point=document.createElement("v:rect");
+			point.setAttribute("fillcolor", series.color);
+			point.setAttribute("strokecolor", series.color);
+			point.setAttribute("title", series.label + ": " + series.values[i].value);
+			point.style.position="absolute";
+			point.style.rotation="45";
+			point.style.top=(y-mod)+"px";
+			point.style.left=(x-mod)+"px";
+			point.style.width=r+"px";
+			point.style.height=r+"px";
+			var fill=document.createElement("v:fill");
+			fill.setAttribute("opacity", "0.5");
+			point.appendChild(fill);
+			chart.dataGroup.appendChild(point);
+		}
+	};	
+	plotters[types.Bubble]=function(series, chart){
+		//	added param for series[n].value: size
+		var minR=1;
+		
+		//	do this off the x axis?
+		var min=chart.properties.axes.x.range.min;
+		var max=chart.properties.axes.x.range.max;
+		var ofst=0-min;
+
+		min+=ofst; max+=ofst;
+		var xmin=chart.properties.padding.left;
+		var xmax=chart.properties.width-chart.properties.padding.right;
+		var factor=(max-min)/(xmax-xmin)*25;
+		
+		for (var i=0; i<series.values.length; i++){
+			var size = series.values[i].size;
+			if (isNaN(parseFloat(size))) size=minR;
+			var mod=(parseFloat(size)*factor)/2;
+
+			var point=document.createElement("v:oval");
+			point.setAttribute("strokecolor", series.color);
+			point.setAttribute("fillcolor", series.color);
+			point.setAttribute("title", series.label + ": " + series.values[i].value + " (" + size + ")");
+			point.style.position="absolute";
+			point.style.top=(_this.getY(series.values[i].value, chart)-mod) + "px";
+			point.style.left=(_this.getX(series.values[i].x, chart)-mod) + "px";
+			point.style.width=mod+"px";
+			point.style.height=mod+"px";
+			chart.dataGroup.appendChild(point);
+		}
+	};
+}();


Property changes on: trunk/PhotoOp/root/static/js/src/widget/vml/Chart.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/xml/Parse.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/xml/Parse.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/xml/Parse.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,188 @@
+/*
+	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.xml.Parse");
+
+dojo.require("dojo.dom");
+
+//TODO: determine dependencies
+// currently has dependency on dojo.xml.DomUtil nodeTypes constants...
+
+/* generic method for taking a node and parsing it into an object
+
+TODO: WARNING: This comment is wrong!
+
+For example, the following xml fragment
+
+<foo bar="bar">
+	<baz xyzzy="xyzzy"/>
+</foo>
+
+can be described as:
+
+dojo.???.foo = {}
+dojo.???.foo.bar = {}
+dojo.???.foo.bar.value = "bar";
+dojo.???.foo.baz = {}
+dojo.???.foo.baz.xyzzy = {}
+dojo.???.foo.baz.xyzzy.value = "xyzzy"
+
+*/
+// using documentFragment nomenclature to generalize in case we don't want to require passing a collection of nodes with a single parent
+dojo.xml.Parse = function(){
+
+	function getDojoTagName (node) {
+		var tagName = node.tagName;
+		if (tagName.substr(0,5).toLowerCase() != "dojo:") {
+			
+			if (tagName.substr(0,4).toLowerCase() == "dojo") {
+				// FIXME: this assuumes tag names are always lower case
+				return "dojo:" + tagName.substring(4).toLowerCase();
+			}
+		
+			// allow lower-casing
+			var djt = node.getAttribute("dojoType") || node.getAttribute("dojotype");
+			if (djt) { return "dojo:" + djt.toLowerCase(); }
+			
+			if (node.getAttributeNS && node.getAttributeNS(dojo.dom.dojoml,"type")) {
+				return "dojo:" + node.getAttributeNS(dojo.dom.dojoml,"type").toLowerCase();
+			}
+			try {
+				// FIXME: IE really really doesn't like this, so we squelch
+				// errors for it
+				djt = node.getAttribute("dojo:type");
+			} catch (e) { /* FIXME: log? */ }
+
+			if (djt) { return "dojo:"+djt.toLowerCase(); }
+		
+			if (!dj_global["djConfig"] || !djConfig["ignoreClassNames"]) {
+				// FIXME: should we make this optionally enabled via djConfig?
+				var classes = node.className||node.getAttribute("class");
+				// FIXME: following line, without check for existence of classes.indexOf
+				// breaks firefox 1.5's svg widgets
+				if (classes && classes.indexOf && classes.indexOf("dojo-") != -1) {
+					var aclasses = classes.split(" ");
+					for(var x=0; x<aclasses.length; x++){
+						if (aclasses[x].length > 5 && aclasses[x].indexOf("dojo-") >= 0) {
+							return "dojo:"+aclasses[x].substr(5).toLowerCase();
+						}
+					}
+				}
+			}
+		
+		}
+		return tagName.toLowerCase();
+	}
+
+	this.parseElement = function(node, hasParentNodeSet, optimizeForDojoML, thisIdx){
+
+        // if parseWidgets="false" don't search inside this node for widgets
+        if (node.getAttribute("parseWidgets") == "false") {
+            return {};
+        }
+
+		// TODO: make this namespace aware
+		var parsedNodeSet = {};
+
+		var tagName = getDojoTagName(node);
+		parsedNodeSet[tagName] = [];
+		if((!optimizeForDojoML)||(tagName.substr(0,4).toLowerCase()=="dojo")){
+			var attributeSet = parseAttributes(node);
+			for(var attr in attributeSet){
+				if((!parsedNodeSet[tagName][attr])||(typeof parsedNodeSet[tagName][attr] != "array")){
+					parsedNodeSet[tagName][attr] = [];
+				}
+				parsedNodeSet[tagName][attr].push(attributeSet[attr]);
+			}
+	
+			// FIXME: we might want to make this optional or provide cloning instead of
+			// referencing, but for now, we include a node reference to allow
+			// instantiated components to figure out their "roots"
+			parsedNodeSet[tagName].nodeRef = node;
+			parsedNodeSet.tagName = tagName;
+			parsedNodeSet.index = thisIdx||0;
+		}
+	
+		var count = 0;
+		var tcn, i = 0, nodes = node.childNodes;
+		while(tcn = nodes[i++]){
+			switch(tcn.nodeType){
+				case  dojo.dom.ELEMENT_NODE: // element nodes, call this function recursively
+					count++;
+					var ctn = getDojoTagName(tcn);
+					if(!parsedNodeSet[ctn]){
+						parsedNodeSet[ctn] = [];
+					}
+					parsedNodeSet[ctn].push(this.parseElement(tcn, true, optimizeForDojoML, count));
+					if(	(tcn.childNodes.length == 1)&&
+						(tcn.childNodes.item(0).nodeType == dojo.dom.TEXT_NODE)){
+						parsedNodeSet[ctn][parsedNodeSet[ctn].length-1].value = tcn.childNodes.item(0).nodeValue;
+					}
+					break;
+				case  dojo.dom.TEXT_NODE: // if a single text node is the child, treat it as an attribute
+					if(node.childNodes.length == 1) {
+						parsedNodeSet[tagName].push({ value: node.childNodes.item(0).nodeValue });
+					}
+					break;
+				default: break;
+				/*
+				case  dojo.dom.ATTRIBUTE_NODE: // attribute node... not meaningful here
+					break;
+				case  dojo.dom.CDATA_SECTION_NODE: // cdata section... not sure if this would ever be meaningful... might be...
+					break;
+				case  dojo.dom.ENTITY_REFERENCE_NODE: // entity reference node... not meaningful here
+					break;
+				case  dojo.dom.ENTITY_NODE: // entity node... not sure if this would ever be meaningful
+					break;
+				case  dojo.dom.PROCESSING_INSTRUCTION_NODE: // processing instruction node... not meaningful here
+					break;
+				case  dojo.dom.COMMENT_NODE: // comment node... not not sure if this would ever be meaningful 
+					break;
+				case  dojo.dom.DOCUMENT_NODE: // document node... not sure if this would ever be meaningful
+					break;
+				case  dojo.dom.DOCUMENT_TYPE_NODE: // document type node... not meaningful here
+					break;
+				case  dojo.dom.DOCUMENT_FRAGMENT_NODE: // document fragment node... not meaningful here
+					break;
+				case  dojo.dom.NOTATION_NODE:// notation node... not meaningful here
+					break;
+				*/
+			}
+		}
+		//return (hasParentNodeSet) ? parsedNodeSet[node.tagName] : parsedNodeSet;
+		return parsedNodeSet;
+	}
+
+	/* parses a set of attributes on a node into an object tree */
+	function parseAttributes(node) {
+		// TODO: make this namespace aware
+		var parsedAttributeSet = {};
+		var atts = node.attributes;
+		// TODO: should we allow for duplicate attributes at this point...
+		// would any of the relevant dom implementations even allow this?
+		var attnode, i=0;
+		while(attnode=atts[i++]) {
+			if((dojo.render.html.capable)&&(dojo.render.html.ie)){
+				if(!attnode){ continue; }
+				if(	(typeof attnode == "object")&&
+					(typeof attnode.nodeValue == 'undefined')||
+					(attnode.nodeValue == null)||
+					(attnode.nodeValue == '')){ 
+					continue; 
+				}
+			}
+			var nn = (attnode.nodeName.indexOf("dojo:") == -1) ? attnode.nodeName : attnode.nodeName.split("dojo:")[1];
+			parsedAttributeSet[nn] = { 
+				value: attnode.nodeValue 
+			};
+		}
+		return parsedAttributeSet;
+	}
+}


Property changes on: trunk/PhotoOp/root/static/js/src/xml/Parse.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/xml/__package__.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/xml/__package__.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/xml/__package__.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,18 @@
+/*
+	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.xml.Parse");
+dojo.kwCompoundRequire({
+	common:		["dojo.xml.domUtil"],
+    browser: 	["dojo.xml.htmlUtil"],
+    dashboard: 	["dojo.xml.htmlUtil"],
+    svg: 		["dojo.xml.svgUtil"]
+});
+dojo.provide("dojo.xml.*");


Property changes on: trunk/PhotoOp/root/static/js/src/xml/__package__.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/xml/domUtil.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/xml/domUtil.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/xml/domUtil.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,84 @@
+/*
+	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.xml.domUtil");
+dojo.require("dojo.graphics.color");
+dojo.require("dojo.dom");
+dojo.require("dojo.style");
+
+dojo.deprecated("dojo.xml.domUtil", "use dojo.dom instead", "0.4");
+
+// for loading script:
+dojo.xml.domUtil = new function(){
+	this.nodeTypes = {
+		ELEMENT_NODE                  : 1,
+		ATTRIBUTE_NODE                : 2,
+		TEXT_NODE                     : 3,
+		CDATA_SECTION_NODE            : 4,
+		ENTITY_REFERENCE_NODE         : 5,
+		ENTITY_NODE                   : 6,
+		PROCESSING_INSTRUCTION_NODE   : 7,
+		COMMENT_NODE                  : 8,
+		DOCUMENT_NODE                 : 9,
+		DOCUMENT_TYPE_NODE            : 10,
+		DOCUMENT_FRAGMENT_NODE        : 11,
+		NOTATION_NODE                 : 12
+	}
+	
+	this.dojoml = "http://www.dojotoolkit.org/2004/dojoml";
+	this.idIncrement = 0;
+	
+	this.getTagName = function(){return dojo.dom.getTagName.apply(dojo.dom, arguments);}
+	this.getUniqueId = function(){return dojo.dom.getUniqueId.apply(dojo.dom, arguments);}
+	this.getFirstChildTag = function() {return dojo.dom.getFirstChildElement.apply(dojo.dom, arguments);}
+	this.getLastChildTag = function() {return dojo.dom.getLastChildElement.apply(dojo.dom, arguments);}
+	this.getNextSiblingTag = function() {return dojo.dom.getNextSiblingElement.apply(dojo.dom, arguments);}
+	this.getPreviousSiblingTag = function() {return dojo.dom.getPreviousSiblingElement.apply(dojo.dom, arguments);}
+
+	this.forEachChildTag = function(node, unaryFunc) {
+		var child = this.getFirstChildTag(node);
+		while(child) {
+			if(unaryFunc(child) == "break") { break; }
+			child = this.getNextSiblingTag(child);
+		}
+	}
+
+	this.moveChildren = function() {return dojo.dom.moveChildren.apply(dojo.dom, arguments);}
+	this.copyChildren = function() {return dojo.dom.copyChildren.apply(dojo.dom, arguments);}
+	this.clearChildren = function() {return dojo.dom.removeChildren.apply(dojo.dom, arguments);}
+	this.replaceChildren = function() {return dojo.dom.replaceChildren.apply(dojo.dom, arguments);}
+
+	this.getStyle = function() {return dojo.style.getStyle.apply(dojo.style, arguments);}
+	this.toCamelCase = function() {return dojo.style.toCamelCase.apply(dojo.style, arguments);}
+	this.toSelectorCase = function() {return dojo.style.toSelectorCase.apply(dojo.style, arguments);}
+
+	this.getAncestors = function(){return dojo.dom.getAncestors.apply(dojo.dom, arguments);}
+	this.isChildOf = function() {return dojo.dom.isDescendantOf.apply(dojo.dom, arguments);}
+	this.createDocumentFromText = function() {return dojo.dom.createDocumentFromText.apply(dojo.dom, arguments);}
+
+	if(dojo.render.html.capable || dojo.render.svg.capable) {
+		this.createNodesFromText = function(txt, wrap){return dojo.dom.createNodesFromText.apply(dojo.dom, arguments);}
+	}
+
+	this.extractRGB = function(color) { return dojo.graphics.color.extractRGB(color); }
+	this.hex2rgb = function(hex) { return dojo.graphics.color.hex2rgb(hex); }
+	this.rgb2hex = function(r, g, b) { return dojo.graphics.color.rgb2hex(r, g, b); }
+
+	this.insertBefore = function() {return dojo.dom.insertBefore.apply(dojo.dom, arguments);}
+	this.before = this.insertBefore;
+	this.insertAfter = function() {return dojo.dom.insertAfter.apply(dojo.dom, arguments);}
+	this.after = this.insertAfter
+	this.insert = function(){return dojo.dom.insertAtPosition.apply(dojo.dom, arguments);}
+	this.insertAtIndex = function(){return dojo.dom.insertAtIndex.apply(dojo.dom, arguments);}
+	this.textContent = function () {return dojo.dom.textContent.apply(dojo.dom, arguments);}
+	this.renderedTextContent = function () {return dojo.dom.renderedTextContent.apply(dojo.dom, arguments);}
+	this.remove = function (node) {return dojo.dom.removeNode.apply(dojo.dom, arguments);}
+}
+


Property changes on: trunk/PhotoOp/root/static/js/src/xml/domUtil.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/xml/htmlUtil.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/xml/htmlUtil.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/xml/htmlUtil.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,121 @@
+/*
+	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.xml.htmlUtil");
+dojo.require("dojo.html");
+dojo.require("dojo.style");
+dojo.require("dojo.dom");
+
+dojo.deprecated("dojo.xml.htmlUtil", "use dojo.html instead", "0.4");
+
+dojo.xml.htmlUtil = new function(){
+	this.styleSheet = dojo.style.styleSheet;
+	
+	this._clobberSelection = function(){return dojo.html.clearSelection.apply(dojo.html, arguments);}
+	this.disableSelect = function(){return dojo.html.disableSelection.apply(dojo.html, arguments);}
+	this.enableSelect = function(){return dojo.html.enableSelection.apply(dojo.html, arguments);}
+	
+	this.getInnerWidth = function(){return dojo.style.getInnerWidth.apply(dojo.style, arguments);}
+	
+	this.getOuterWidth = function(node){
+		dojo.unimplemented("dojo.xml.htmlUtil.getOuterWidth");
+	}
+
+	this.getInnerHeight = function(){return dojo.style.getInnerHeight.apply(dojo.style, arguments);}
+
+	this.getOuterHeight = function(node){
+		dojo.unimplemented("dojo.xml.htmlUtil.getOuterHeight");
+	}
+
+	this.getTotalOffset = function(){return dojo.style.getTotalOffset.apply(dojo.style, arguments);}
+	this.totalOffsetLeft = function(){return dojo.style.totalOffsetLeft.apply(dojo.style, arguments);}
+
+	this.getAbsoluteX = this.totalOffsetLeft;
+
+	this.totalOffsetTop = function(){return dojo.style.totalOffsetTop.apply(dojo.style, arguments);}
+	
+	this.getAbsoluteY = this.totalOffsetTop;
+
+	this.getEventTarget = function(){return dojo.html.getEventTarget.apply(dojo.html, arguments);}
+	this.getScrollTop = function() {return dojo.html.getScrollTop.apply(dojo.html, arguments);}
+	this.getScrollLeft = function() {return dojo.html.getScrollLeft.apply(dojo.html, arguments);}
+
+	this.evtTgt = this.getEventTarget;
+
+	this.getParentOfType = function(){return dojo.html.getParentOfType.apply(dojo.html, arguments);}
+	this.getAttribute = function(){return dojo.html.getAttribute.apply(dojo.html, arguments);}
+	this.getAttr = function (node, attr) { // for backwards compat (may disappear!!!)
+		dojo.deprecated("dojo.xml.htmlUtil.getAttr", "use dojo.xml.htmlUtil.getAttribute instead", "0.4");
+		return dojo.xml.htmlUtil.getAttribute(node, attr);
+	}
+	this.hasAttribute = function(){return dojo.html.hasAttribute.apply(dojo.html, arguments);}
+
+	this.hasAttr = function (node, attr) { // for backwards compat (may disappear!!!)
+		dojo.deprecated("dojo.xml.htmlUtil.hasAttr", "use dojo.xml.htmlUtil.hasAttribute instead", "0.4");
+		return dojo.xml.htmlUtil.hasAttribute(node, attr);
+	}
+	
+	this.getClass = function(){return dojo.html.getClass.apply(dojo.html, arguments)}
+	this.hasClass = function(){return dojo.html.hasClass.apply(dojo.html, arguments)}
+	this.prependClass = function(){return dojo.html.prependClass.apply(dojo.html, arguments)}
+	this.addClass = function(){return dojo.html.addClass.apply(dojo.html, arguments)}
+	this.setClass = function(){return dojo.html.setClass.apply(dojo.html, arguments)}
+	this.removeClass = function(){return dojo.html.removeClass.apply(dojo.html, arguments)}
+
+	// Enum type for getElementsByClass classMatchType arg:
+	this.classMatchType = {
+		ContainsAll : 0, // all of the classes are part of the node's class (default)
+		ContainsAny : 1, // any of the classes are part of the node's class
+		IsOnly : 2 // only all of the classes are part of the node's class
+	}
+
+	this.getElementsByClass = function() {return dojo.html.getElementsByClass.apply(dojo.html, arguments)}
+	this.getElementsByClassName = this.getElementsByClass;
+	
+	this.setOpacity = function() {return dojo.style.setOpacity.apply(dojo.style, arguments)}
+	this.getOpacity = function() {return dojo.style.getOpacity.apply(dojo.style, arguments)}
+	this.clearOpacity = function() {return dojo.style.clearOpacity.apply(dojo.style, arguments)}
+	
+	this.gravity = function(){return dojo.html.gravity.apply(dojo.html, arguments)}
+	
+	this.gravity.NORTH = 1;
+	this.gravity.SOUTH = 1 << 1;
+	this.gravity.EAST = 1 << 2;
+	this.gravity.WEST = 1 << 3;
+	
+	this.overElement = function(){return dojo.html.overElement.apply(dojo.html, arguments)}
+
+	this.insertCssRule = function(){return dojo.style.insertCssRule.apply(dojo.style, arguments)}
+	
+	this.insertCSSRule = function(selector, declaration, index){
+		dojo.deprecated("dojo.xml.htmlUtil.insertCSSRule", "use dojo.style.insertCssRule instead", "0.4");
+		return dojo.xml.htmlUtil.insertCssRule(selector, declaration, index);
+	}
+	
+	this.removeCssRule = function(){return dojo.style.removeCssRule.apply(dojo.style, arguments)}
+
+	this.removeCSSRule = function(index){
+		dojo.deprecated("dojo.xml.htmlUtil.removeCSSRule", "use dojo.xml.htmlUtil.removeCssRule instead", "0.4");
+		return dojo.xml.htmlUtil.removeCssRule(index);
+	}
+
+	this.insertCssFile = function(){return dojo.style.insertCssFile.apply(dojo.style, arguments)}
+
+	this.insertCSSFile = function(URI, doc, checkDuplicates){
+		dojo.deprecated("dojo.xml.htmlUtil.insertCSSFile", "use dojo.xml.htmlUtil.insertCssFile instead", "0.4");
+		return dojo.xml.htmlUtil.insertCssFile(URI, doc, checkDuplicates);
+	}
+
+	this.getBackgroundColor = function() {return dojo.style.getBackgroundColor.apply(dojo.style, arguments)}
+
+	this.getUniqueId = function() { return dojo.dom.getUniqueId(); }
+
+	this.getStyle = function() {return dojo.style.getStyle.apply(dojo.style, arguments)}
+}


Property changes on: trunk/PhotoOp/root/static/js/src/xml/htmlUtil.js
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/PhotoOp/root/static/js/src/xml/svgUtil.js
===================================================================
--- trunk/PhotoOp/root/static/js/src/xml/svgUtil.js	                        (rev 0)
+++ trunk/PhotoOp/root/static/js/src/xml/svgUtil.js	2009-02-07 13:12:54 UTC (rev 5416)
@@ -0,0 +1,32 @@
+/*
+	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.xml.svgUtil");
+// FIXME: add imports for deps!
+
+dojo.xml.svgUtil = new function(){
+
+	this.getInnerWidth = function(node){
+		// FIXME: need to find out from dylan how to 
+	}
+
+	this.getOuterWidth = function(node){
+		
+	}
+
+	this.getInnerHeight = function(node){
+		
+	}
+
+	this.getOuterHeight = function(node){
+		
+	}
+
+}


Property changes on: trunk/PhotoOp/root/static/js/src/xml/svgUtil.js
___________________________________________________________________
Name: svn:executable
   + *




More information about the Bast-commits mailing list